@opensumi/ide-terminal-next 3.7.1 → 3.7.2-next-1739859371.0
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/browser/component/resize.view.d.ts.map +1 -1
- package/lib/browser/component/resize.view.js +2 -2
- package/lib/browser/component/resize.view.js.map +1 -1
- package/lib/browser/component/search.module.less +61 -0
- package/lib/browser/component/search.view.d.ts +3 -0
- package/lib/browser/component/search.view.d.ts.map +1 -0
- package/lib/browser/component/search.view.js +89 -0
- package/lib/browser/component/search.view.js.map +1 -0
- package/lib/browser/component/tab.item.d.ts.map +1 -1
- package/lib/browser/component/tab.item.js +23 -1
- package/lib/browser/component/tab.item.js.map +1 -1
- package/lib/browser/component/tab.view.d.ts.map +1 -1
- package/lib/browser/component/tab.view.js +10 -1
- package/lib/browser/component/tab.view.js.map +1 -1
- package/lib/browser/component/terminal.module.less +3 -27
- package/lib/browser/component/terminal.view.d.ts.map +1 -1
- package/lib/browser/component/terminal.view.js +2 -32
- package/lib/browser/component/terminal.view.js.map +1 -1
- package/lib/browser/component/terminal.widget.d.ts.map +1 -1
- package/lib/browser/component/terminal.widget.js +9 -3
- package/lib/browser/component/terminal.widget.js.map +1 -1
- package/lib/browser/contribution/terminal.view.d.ts.map +1 -1
- package/lib/browser/contribution/terminal.view.js +2 -0
- package/lib/browser/contribution/terminal.view.js.map +1 -1
- package/lib/browser/links/link-manager.d.ts.map +1 -1
- package/lib/browser/links/link-manager.js +10 -7
- package/lib/browser/links/link-manager.js.map +1 -1
- package/lib/browser/links/validated-local-link-provider.d.ts +1 -0
- package/lib/browser/links/validated-local-link-provider.d.ts.map +1 -1
- package/lib/browser/links/validated-local-link-provider.js +55 -42
- package/lib/browser/links/validated-local-link-provider.js.map +1 -1
- package/lib/browser/links/word-link-provider.d.ts +29 -0
- package/lib/browser/links/word-link-provider.d.ts.map +1 -0
- package/lib/browser/links/word-link-provider.js +153 -0
- package/lib/browser/links/word-link-provider.js.map +1 -0
- package/lib/browser/terminal.client.d.ts +7 -1
- package/lib/browser/terminal.client.d.ts.map +1 -1
- package/lib/browser/terminal.client.js +9 -2
- package/lib/browser/terminal.client.js.map +1 -1
- package/lib/browser/terminal.controller.d.ts +1 -2
- package/lib/browser/terminal.controller.d.ts.map +1 -1
- package/lib/browser/terminal.controller.js +6 -6
- package/lib/browser/terminal.controller.js.map +1 -1
- package/lib/browser/terminal.hover.manager.d.ts.map +1 -1
- package/lib/browser/terminal.hover.manager.js +4 -2
- package/lib/browser/terminal.hover.manager.js.map +1 -1
- package/lib/browser/terminal.search.d.ts +9 -1
- package/lib/browser/terminal.search.d.ts.map +1 -1
- package/lib/browser/terminal.search.js +49 -1
- package/lib/browser/terminal.search.js.map +1 -1
- package/lib/browser/terminal.view.d.ts +1 -0
- package/lib/browser/terminal.view.d.ts.map +1 -1
- package/lib/browser/terminal.view.js +13 -0
- package/lib/browser/terminal.view.js.map +1 -1
- package/lib/browser/xterm.d.ts +7 -1
- package/lib/browser/xterm.d.ts.map +1 -1
- package/lib/browser/xterm.js +12 -1
- package/lib/browser/xterm.js.map +1 -1
- package/lib/common/client.d.ts +8 -2
- package/lib/common/client.d.ts.map +1 -1
- package/lib/common/client.js.map +1 -1
- package/lib/common/controller.d.ts +16 -0
- package/lib/common/controller.d.ts.map +1 -1
- package/lib/common/controller.js.map +1 -1
- package/lib/common/pty.d.ts +1 -1
- package/lib/common/pty.d.ts.map +1 -1
- package/lib/common/render.d.ts +3 -0
- package/lib/common/render.d.ts.map +1 -1
- package/lib/common/render.js.map +1 -1
- package/lib/common/xterm.d.ts +8 -2
- package/lib/common/xterm.d.ts.map +1 -1
- package/lib/common/xterm.js.map +1 -1
- package/package.json +18 -18
- package/src/browser/component/resize.view.tsx +2 -1
- package/src/browser/component/search.module.less +61 -0
- package/src/browser/component/search.view.tsx +142 -0
- package/src/browser/component/tab.item.tsx +38 -0
- package/src/browser/component/tab.view.tsx +12 -0
- package/src/browser/component/terminal.module.less +3 -27
- package/src/browser/component/terminal.view.tsx +3 -56
- package/src/browser/component/terminal.widget.tsx +8 -3
- package/src/browser/contribution/terminal.view.ts +2 -0
- package/src/browser/links/link-manager.ts +11 -8
- package/src/browser/links/validated-local-link-provider.ts +68 -48
- package/src/browser/links/word-link-provider.ts +175 -0
- package/src/browser/terminal.client.ts +13 -2
- package/src/browser/terminal.controller.ts +7 -8
- package/src/browser/terminal.hover.manager.ts +4 -4
- package/src/browser/terminal.search.ts +49 -2
- package/src/browser/terminal.view.ts +17 -0
- package/src/browser/xterm.ts +14 -1
- package/src/common/client.ts +7 -2
- package/src/common/controller.ts +20 -0
- package/src/common/pty.ts +1 -1
- package/src/common/render.ts +3 -0
- package/src/common/xterm-private.d.ts +7 -4
- package/src/common/xterm.ts +6 -2
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
winLineAndColumnMatchIndex,
|
|
42
42
|
winLocalLinkClause,
|
|
43
43
|
} from './validated-local-link-provider';
|
|
44
|
+
import { TerminalWordLinkProvider } from './word-link-provider';
|
|
44
45
|
|
|
45
46
|
const { posix, win32 } = path;
|
|
46
47
|
|
|
@@ -136,6 +137,13 @@ export class TerminalLinkManager extends Disposable {
|
|
|
136
137
|
]);
|
|
137
138
|
this._standardLinkProviders.push(validatedProvider);
|
|
138
139
|
|
|
140
|
+
const wordLinkProvider = this.injector.get(TerminalWordLinkProvider, [
|
|
141
|
+
this._xterm,
|
|
142
|
+
async (link, cb) => cb(await this._resolvePath(link)),
|
|
143
|
+
wrappedTextLinkActivateCallback,
|
|
144
|
+
]);
|
|
145
|
+
this._standardLinkProviders.push(wordLinkProvider);
|
|
146
|
+
|
|
139
147
|
this._registerStandardLinkProviders();
|
|
140
148
|
}
|
|
141
149
|
|
|
@@ -165,8 +173,8 @@ export class TerminalLinkManager extends Disposable {
|
|
|
165
173
|
) {
|
|
166
174
|
const core = (this._xterm as any)._core as XTermCore;
|
|
167
175
|
const cellDimensions = {
|
|
168
|
-
width: core._renderService.dimensions.
|
|
169
|
-
height: core._renderService.dimensions.
|
|
176
|
+
width: core._renderService.dimensions.css.cell.width,
|
|
177
|
+
height: core._renderService.dimensions.css.cell.height,
|
|
170
178
|
};
|
|
171
179
|
const terminalDimensions = {
|
|
172
180
|
width: this._xterm.cols,
|
|
@@ -375,13 +383,8 @@ export class TerminalLinkManager extends Disposable {
|
|
|
375
383
|
return undefined;
|
|
376
384
|
}
|
|
377
385
|
|
|
378
|
-
const linkUrl = this.extractLinkUrl(preprocessedLink);
|
|
379
|
-
if (!linkUrl) {
|
|
380
|
-
return undefined;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
386
|
try {
|
|
384
|
-
const uri = URI.file(
|
|
387
|
+
const uri = URI.file(preprocessedLink);
|
|
385
388
|
const stat = await this._fileService.getFileStat(uri.toString());
|
|
386
389
|
if (stat) {
|
|
387
390
|
return { uri, isDirectory: stat.isDirectory };
|
|
@@ -138,7 +138,6 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider
|
|
|
138
138
|
let match;
|
|
139
139
|
let stringIndex = -1;
|
|
140
140
|
while ((match = rex.exec(text)) !== null) {
|
|
141
|
-
// const link = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex];
|
|
142
141
|
let link = match[0];
|
|
143
142
|
if (!link) {
|
|
144
143
|
// something matched but does not comply with the given matchIndex
|
|
@@ -169,60 +168,81 @@ export class TerminalValidatedLocalLinkProvider extends TerminalBaseLinkProvider
|
|
|
169
168
|
link = link.substring(2);
|
|
170
169
|
stringIndex += 2;
|
|
171
170
|
}
|
|
171
|
+
const validatedLinks = await this.detectLocalLink(link, lines, startLine, stringIndex, 1);
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
},
|
|
183
|
-
startLine,
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
const validatedLink = await new Promise<TerminalLink | undefined>((r) => {
|
|
187
|
-
this._validationCallback(link, async (result) => {
|
|
188
|
-
if (result) {
|
|
189
|
-
const label = result.isDirectory
|
|
190
|
-
? (await this._isDirectoryInsideWorkspace(result.uri))
|
|
191
|
-
? FOLDER_IN_WORKSPACE_LABEL
|
|
192
|
-
: FOLDER_NOT_IN_WORKSPACE_LABEL
|
|
193
|
-
: OPEN_FILE_LABEL;
|
|
194
|
-
const activateCallback = this._wrapLinkHandler((event: MouseEvent | undefined, text: string) => {
|
|
195
|
-
if (result.isDirectory) {
|
|
196
|
-
this._handleLocalFolderLink(result.uri);
|
|
197
|
-
} else {
|
|
198
|
-
this._activateFileCallback(event, text);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
r(
|
|
202
|
-
this.injector.get(TerminalLink, [
|
|
203
|
-
this._xterm,
|
|
204
|
-
bufferRange,
|
|
205
|
-
link,
|
|
206
|
-
this._xterm.buffer.active.viewportY,
|
|
207
|
-
activateCallback,
|
|
208
|
-
this._tooltipCallback,
|
|
209
|
-
true,
|
|
210
|
-
label,
|
|
211
|
-
]),
|
|
212
|
-
);
|
|
213
|
-
} else {
|
|
214
|
-
r(undefined);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
if (validatedLink) {
|
|
219
|
-
result.push(validatedLink);
|
|
173
|
+
if (validatedLinks.length > 0) {
|
|
174
|
+
result.push(...validatedLinks);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (result.length === 0) {
|
|
179
|
+
const validatedLinks = await this.detectLocalLink(text, lines, startLine, stringIndex, 2);
|
|
180
|
+
if (validatedLinks.length > 0) {
|
|
181
|
+
result.push(...validatedLinks);
|
|
220
182
|
}
|
|
221
183
|
}
|
|
222
184
|
|
|
223
185
|
return result;
|
|
224
186
|
}
|
|
225
187
|
|
|
188
|
+
private async detectLocalLink(
|
|
189
|
+
text: string,
|
|
190
|
+
bufferLines: IBufferLine[],
|
|
191
|
+
startLine: number,
|
|
192
|
+
stringIndex: number,
|
|
193
|
+
offset,
|
|
194
|
+
) {
|
|
195
|
+
const result: TerminalLink[] = [];
|
|
196
|
+
const validatedLink = await new Promise<TerminalLink | undefined>((r) => {
|
|
197
|
+
this._validationCallback(text, async (result) => {
|
|
198
|
+
if (result) {
|
|
199
|
+
const label = result.isDirectory
|
|
200
|
+
? (await this._isDirectoryInsideWorkspace(result.uri))
|
|
201
|
+
? FOLDER_IN_WORKSPACE_LABEL
|
|
202
|
+
: FOLDER_NOT_IN_WORKSPACE_LABEL
|
|
203
|
+
: OPEN_FILE_LABEL;
|
|
204
|
+
const activateCallback = this._wrapLinkHandler((event: MouseEvent | undefined, text: string) => {
|
|
205
|
+
if (result.isDirectory) {
|
|
206
|
+
this._handleLocalFolderLink(result.uri);
|
|
207
|
+
} else {
|
|
208
|
+
this._activateFileCallback(event, text);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
// Convert the link text's string index into a wrapped buffer range
|
|
212
|
+
const bufferRange = convertLinkRangeToBuffer(
|
|
213
|
+
bufferLines,
|
|
214
|
+
this._xterm.cols,
|
|
215
|
+
{
|
|
216
|
+
startColumn: stringIndex + 1,
|
|
217
|
+
startLineNumber: 1,
|
|
218
|
+
endColumn: stringIndex + text.length + offset,
|
|
219
|
+
endLineNumber: 1,
|
|
220
|
+
},
|
|
221
|
+
startLine,
|
|
222
|
+
);
|
|
223
|
+
r(
|
|
224
|
+
this.injector.get(TerminalLink, [
|
|
225
|
+
this._xterm,
|
|
226
|
+
bufferRange,
|
|
227
|
+
text,
|
|
228
|
+
this._xterm.buffer.active.viewportY,
|
|
229
|
+
activateCallback,
|
|
230
|
+
this._tooltipCallback,
|
|
231
|
+
true,
|
|
232
|
+
label,
|
|
233
|
+
]),
|
|
234
|
+
);
|
|
235
|
+
} else {
|
|
236
|
+
r(undefined);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
if (validatedLink) {
|
|
241
|
+
result.push(validatedLink);
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
226
246
|
protected get _localLinkRegex(): RegExp {
|
|
227
247
|
const baseLocalLinkClause = this._client.os === OperatingSystem.Windows ? winLocalLinkClause : unixLocalLinkClause;
|
|
228
248
|
// Append line and column number regex
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { IBufferLine, Terminal } from '@xterm/xterm';
|
|
2
|
+
|
|
3
|
+
import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
|
|
4
|
+
import { PrefixQuickOpenService } from '@opensumi/ide-core-browser/lib/quick-open';
|
|
5
|
+
import { AppConfig } from '@opensumi/ide-core-browser/lib/react-providers/config-provider';
|
|
6
|
+
import { IWindowService } from '@opensumi/ide-core-browser/lib/window';
|
|
7
|
+
import { URI } from '@opensumi/ide-core-common';
|
|
8
|
+
import { CommandService } from '@opensumi/ide-core-common/lib/command';
|
|
9
|
+
import { IWorkspaceService } from '@opensumi/ide-workspace/lib/common/workspace.interface';
|
|
10
|
+
|
|
11
|
+
import { escapeRegExpCharacters } from '../terminal.typeAhead.addon';
|
|
12
|
+
|
|
13
|
+
import { TerminalBaseLinkProvider } from './base';
|
|
14
|
+
import { convertLinkRangeToBuffer, getXtermLineContent } from './helpers';
|
|
15
|
+
import { TerminalLink } from './link';
|
|
16
|
+
|
|
17
|
+
export const USUAL_WORD_SEPARATORS = ' ()[]{}\',"`─‘’“”|';
|
|
18
|
+
|
|
19
|
+
interface Word {
|
|
20
|
+
startIndex: number;
|
|
21
|
+
endIndex: number;
|
|
22
|
+
text: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Injectable({ multiple: true })
|
|
26
|
+
export class TerminalWordLinkProvider extends TerminalBaseLinkProvider {
|
|
27
|
+
private _separatorRegex!: RegExp;
|
|
28
|
+
|
|
29
|
+
@Autowired(AppConfig)
|
|
30
|
+
private readonly appConfig: AppConfig;
|
|
31
|
+
|
|
32
|
+
@Autowired(CommandService)
|
|
33
|
+
private readonly commandService: CommandService;
|
|
34
|
+
|
|
35
|
+
@Autowired(IWindowService)
|
|
36
|
+
protected readonly windowService: IWindowService;
|
|
37
|
+
|
|
38
|
+
@Autowired(INJECTOR_TOKEN)
|
|
39
|
+
private readonly injector: Injector;
|
|
40
|
+
|
|
41
|
+
@Autowired(IWorkspaceService)
|
|
42
|
+
private readonly workspaceService: IWorkspaceService;
|
|
43
|
+
|
|
44
|
+
@Autowired(PrefixQuickOpenService)
|
|
45
|
+
protected readonly quickOpenService: PrefixQuickOpenService;
|
|
46
|
+
|
|
47
|
+
constructor(
|
|
48
|
+
private readonly _xterm: Terminal,
|
|
49
|
+
private readonly _validationCallback: (
|
|
50
|
+
link: string,
|
|
51
|
+
callback: (result: { uri: URI; isDirectory: boolean } | undefined) => void,
|
|
52
|
+
) => void,
|
|
53
|
+
private readonly _activateFileCallback: (event: MouseEvent | undefined, link: string) => void,
|
|
54
|
+
) {
|
|
55
|
+
super();
|
|
56
|
+
this._refreshSeparatorCodes();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private _refreshSeparatorCodes(): void {
|
|
60
|
+
let powerlineSymbols = '';
|
|
61
|
+
for (let i = 0xe0b0; i <= 0xe0bf; i++) {
|
|
62
|
+
powerlineSymbols += String.fromCharCode(i);
|
|
63
|
+
}
|
|
64
|
+
this._separatorRegex = new RegExp(`[${escapeRegExpCharacters(USUAL_WORD_SEPARATORS)}${powerlineSymbols}]`, 'g');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected _provideLinks(bufferLineNumber: number): Promise<TerminalLink[]> | TerminalLink[] {
|
|
68
|
+
const links: TerminalLink[] = [];
|
|
69
|
+
const startLine = bufferLineNumber - 1;
|
|
70
|
+
const endLine = startLine;
|
|
71
|
+
|
|
72
|
+
const lines: IBufferLine[] = [this._xterm.buffer.active.getLine(startLine)!];
|
|
73
|
+
|
|
74
|
+
const text = getXtermLineContent(this._xterm.buffer.active, startLine, endLine, this._xterm.cols);
|
|
75
|
+
if (text === '' || text.length > 1024) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Parse out all words from the wrapped line
|
|
80
|
+
const words: Word[] = this._parseWords(text);
|
|
81
|
+
|
|
82
|
+
// Map the words to ITerminalLink objects
|
|
83
|
+
for (const word of words) {
|
|
84
|
+
if (word.text === '') {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (word.text.length > 0 && word.text.charAt(word.text.length - 1) === ':') {
|
|
89
|
+
word.text = word.text.slice(0, -1);
|
|
90
|
+
word.endIndex--;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const bufferRange = convertLinkRangeToBuffer(
|
|
94
|
+
lines,
|
|
95
|
+
this._xterm.cols,
|
|
96
|
+
{
|
|
97
|
+
startColumn: word.startIndex + 1,
|
|
98
|
+
startLineNumber: 1,
|
|
99
|
+
endColumn: word.endIndex + 1,
|
|
100
|
+
endLineNumber: 1,
|
|
101
|
+
},
|
|
102
|
+
startLine,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Search links
|
|
106
|
+
const activateHandler = (event: MouseEvent | undefined, text: string) => {
|
|
107
|
+
this._validationCallback(text, async (result) => {
|
|
108
|
+
if (result) {
|
|
109
|
+
if (result.isDirectory) {
|
|
110
|
+
this._handleLocalFolderLink(result.uri);
|
|
111
|
+
} else {
|
|
112
|
+
this._activateFileCallback(event, text);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
this.quickOpenService.open(text);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
links.push(
|
|
121
|
+
this.injector.get(TerminalLink, [
|
|
122
|
+
this._xterm,
|
|
123
|
+
bufferRange,
|
|
124
|
+
word.text,
|
|
125
|
+
this._xterm.buffer.active.viewportY,
|
|
126
|
+
activateHandler,
|
|
127
|
+
undefined,
|
|
128
|
+
true,
|
|
129
|
+
word.text,
|
|
130
|
+
]),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return links;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async _handleLocalFolderLink(uri: URI): Promise<void> {
|
|
138
|
+
// If the folder is within one of the window's workspaces, focus it in the explorer
|
|
139
|
+
if (await this._isDirectoryInsideWorkspace(uri)) {
|
|
140
|
+
await this.commandService.executeCommand('revealInExplorer', uri);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Open a new window for the folder
|
|
145
|
+
if (this.appConfig.isElectronRenderer) {
|
|
146
|
+
this.windowService.openWorkspace(uri, { newWindow: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private _parseWords(text: string): Word[] {
|
|
151
|
+
const words: Word[] = [];
|
|
152
|
+
const splitWords = text.split(this._separatorRegex);
|
|
153
|
+
let runningIndex = 0;
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
155
|
+
for (let i = 0; i < splitWords.length; i++) {
|
|
156
|
+
words.push({
|
|
157
|
+
text: splitWords[i],
|
|
158
|
+
startIndex: runningIndex,
|
|
159
|
+
endIndex: runningIndex + splitWords[i].length,
|
|
160
|
+
});
|
|
161
|
+
runningIndex += splitWords[i].length + 1;
|
|
162
|
+
}
|
|
163
|
+
return words;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private async _isDirectoryInsideWorkspace(uri: URI) {
|
|
167
|
+
const folders = await this.workspaceService.roots;
|
|
168
|
+
for (const folder of folders) {
|
|
169
|
+
if (URI.parse(folder.uri).isEqualOrParent(uri)) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ISearchOptions } from '@xterm/addon-search';
|
|
2
|
+
|
|
1
3
|
import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
|
|
2
4
|
import { IEventBus, QuickPickService, TerminalClientAttachEvent, localize } from '@opensumi/ide-core-browser';
|
|
3
5
|
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences/types';
|
|
@@ -729,9 +731,18 @@ export class TerminalClient extends Disposable implements ITerminalClient {
|
|
|
729
731
|
return this.xterm.raw.paste(text);
|
|
730
732
|
}
|
|
731
733
|
|
|
732
|
-
findNext(text: string) {
|
|
734
|
+
findNext(text: string, searchOptions: ISearchOptions = {}) {
|
|
735
|
+
this._checkReady();
|
|
736
|
+
return this.xterm.findNext(text, searchOptions);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
findPrevious(text: string, searchOptions: ISearchOptions = {}) {
|
|
733
740
|
this._checkReady();
|
|
734
|
-
return this.xterm.
|
|
741
|
+
return this.xterm.findPrevious(text, searchOptions);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
get onSearchResultsChange() {
|
|
745
|
+
return this.xterm.onSearchResultsChange;
|
|
735
746
|
}
|
|
736
747
|
|
|
737
748
|
closeSearch() {
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
} from '@opensumi/ide-core-common';
|
|
24
24
|
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
25
25
|
import { IMainLayoutService } from '@opensumi/ide-main-layout';
|
|
26
|
-
import { TabBarHandler } from '@opensumi/ide-main-layout/lib/browser/tabbar-handler';
|
|
27
26
|
import { IThemeService } from '@opensumi/ide-theme';
|
|
28
27
|
|
|
29
28
|
import {
|
|
@@ -62,7 +61,6 @@ import { TerminalGroupViewService } from './terminal.view';
|
|
|
62
61
|
@Injectable()
|
|
63
62
|
export class TerminalController extends WithEventBus implements ITerminalController {
|
|
64
63
|
protected _focus: boolean;
|
|
65
|
-
protected _tabBarHandler: TabBarHandler | undefined;
|
|
66
64
|
protected _clients: Map<string, ITerminalClient>;
|
|
67
65
|
protected _onDidOpenTerminal = new Emitter<ITerminalInfo>();
|
|
68
66
|
protected _onDidCloseTerminal = new Emitter<ITerminalExitEvent>();
|
|
@@ -199,6 +197,10 @@ export class TerminalController extends WithEventBus implements ITerminalControl
|
|
|
199
197
|
return this._clientId;
|
|
200
198
|
}
|
|
201
199
|
|
|
200
|
+
get _tabBarHandler() {
|
|
201
|
+
return this.layoutService.getTabbarHandler(TERMINAL_CONTAINER_ID);
|
|
202
|
+
}
|
|
203
|
+
|
|
202
204
|
private async _createClientOrIgnore(widget: IWidget) {
|
|
203
205
|
if (this._clients.has(widget.id)) {
|
|
204
206
|
return this._clients.get(widget.id)!;
|
|
@@ -397,11 +399,9 @@ export class TerminalController extends WithEventBus implements ITerminalControl
|
|
|
397
399
|
}
|
|
398
400
|
|
|
399
401
|
initContextKey(dom: HTMLDivElement) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.terminalContextKey.isTerminalViewInitialized.set(true);
|
|
404
|
-
}
|
|
402
|
+
this.terminalContextKey = this.injector.get(TerminalContextKey, [dom]);
|
|
403
|
+
this.terminalContextKey.isTerminalFocused.set(this._focus);
|
|
404
|
+
this.terminalContextKey.isTerminalViewInitialized.set(true);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
async firstInitialize() {
|
|
@@ -418,7 +418,6 @@ export class TerminalController extends WithEventBus implements ITerminalControl
|
|
|
418
418
|
}),
|
|
419
419
|
]);
|
|
420
420
|
|
|
421
|
-
this._tabBarHandler = this.layoutService.getTabbarHandler(TERMINAL_CONTAINER_ID);
|
|
422
421
|
this.themeBackground = this.terminalTheme.terminalTheme.background || '';
|
|
423
422
|
|
|
424
423
|
this.addDispose(
|
|
@@ -14,10 +14,12 @@ export class TerminalHoverManagerService implements ITerminalHoverManagerService
|
|
|
14
14
|
private hoverWidget: HTMLElement | undefined;
|
|
15
15
|
|
|
16
16
|
private appendTerminalHoverOverlay() {
|
|
17
|
-
|
|
17
|
+
let overlayContainer = document.querySelector('#terminal-link-hover-overlay');
|
|
18
18
|
|
|
19
19
|
if (!overlayContainer) {
|
|
20
|
-
|
|
20
|
+
overlayContainer = document.createElement('div');
|
|
21
|
+
overlayContainer.id = 'terminal-link-hover-overlay';
|
|
22
|
+
document.body.appendChild(overlayContainer);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const overlay = document.createElement('div');
|
|
@@ -72,13 +74,11 @@ export class TerminalHoverManagerService implements ITerminalHoverManagerService
|
|
|
72
74
|
this.hoverWidget.style.top = `${
|
|
73
75
|
(viewportRange.start.y - 1) * cellDimensions.height + boundingClientRect.y - TIPS_OFFSET_Y
|
|
74
76
|
}px`;
|
|
75
|
-
|
|
76
77
|
let tooltipsLeft = viewportRange.start.x * cellDimensions.width + boundingClientRect.x + TIPS_OFFSET_X;
|
|
77
78
|
// if the tooltip is too close to the right edge of the terminal, move it to the left
|
|
78
79
|
if (tooltipsLeft + this.hoverWidget.clientWidth > boundingClientRect.x + boundingClientRect.width) {
|
|
79
80
|
tooltipsLeft = boundingClientRect.x + boundingClientRect.width - this.hoverWidget.clientWidth - TIPS_OFFSET_X;
|
|
80
81
|
}
|
|
81
|
-
|
|
82
82
|
this.hoverWidget.style.left = `${tooltipsLeft}px`;
|
|
83
83
|
}
|
|
84
84
|
});
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
2
|
import { Emitter, Event, debounce } from '@opensumi/ide-core-common';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ITerminalClient,
|
|
6
|
+
ITerminalController,
|
|
7
|
+
ITerminalGroupViewService,
|
|
8
|
+
ITerminalSearchService,
|
|
9
|
+
IUIState,
|
|
10
|
+
} from '../common';
|
|
5
11
|
|
|
6
12
|
@Injectable()
|
|
7
13
|
export class TerminalSearchService implements ITerminalSearchService {
|
|
8
14
|
protected _isVisible: boolean = false;
|
|
15
|
+
|
|
16
|
+
public UIState: IUIState = {
|
|
17
|
+
isMatchCase: false,
|
|
18
|
+
isUseRegexp: false,
|
|
19
|
+
isWholeWord: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
9
22
|
get isVisible() {
|
|
10
23
|
return this._isVisible;
|
|
11
24
|
}
|
|
@@ -31,6 +44,10 @@ export class TerminalSearchService implements ITerminalSearchService {
|
|
|
31
44
|
this.isVisible = true;
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
get onResultChange() {
|
|
48
|
+
return this.client?.onSearchResultsChange;
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
close() {
|
|
35
52
|
this.client?.closeSearch();
|
|
36
53
|
this.isVisible = false;
|
|
@@ -45,6 +62,36 @@ export class TerminalSearchService implements ITerminalSearchService {
|
|
|
45
62
|
|
|
46
63
|
@debounce(150)
|
|
47
64
|
search() {
|
|
48
|
-
this.client?.findNext(this.text
|
|
65
|
+
this.client?.findNext(this.text, {
|
|
66
|
+
wholeWord: this.UIState.isWholeWord,
|
|
67
|
+
regex: this.UIState.isUseRegexp,
|
|
68
|
+
caseSensitive: this.UIState.isMatchCase,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@debounce(150)
|
|
73
|
+
searchPrevious() {
|
|
74
|
+
this.client?.findPrevious(this.text, {
|
|
75
|
+
wholeWord: this.UIState.isWholeWord,
|
|
76
|
+
regex: this.UIState.isUseRegexp,
|
|
77
|
+
caseSensitive: this.UIState.isMatchCase,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@debounce(150)
|
|
82
|
+
searchNext(): void {
|
|
83
|
+
this.client?.findNext(this.text, {
|
|
84
|
+
wholeWord: this.UIState.isWholeWord,
|
|
85
|
+
regex: this.UIState.isUseRegexp,
|
|
86
|
+
caseSensitive: this.UIState.isMatchCase,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
updateUIState(state: Partial<IUIState>): void {
|
|
91
|
+
this.UIState = {
|
|
92
|
+
...this.UIState,
|
|
93
|
+
...state,
|
|
94
|
+
};
|
|
95
|
+
this.search();
|
|
49
96
|
}
|
|
50
97
|
}
|
|
@@ -275,6 +275,23 @@ export class TerminalGroupViewService implements ITerminalGroupViewService {
|
|
|
275
275
|
return this.groups.get()[index];
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
swapGroup(i: number, j: number) {
|
|
279
|
+
const groups = this.groups.get();
|
|
280
|
+
|
|
281
|
+
if (i === -1 || j === -1) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const newGroups = [...groups];
|
|
286
|
+
const temp = newGroups[i];
|
|
287
|
+
newGroups[i] = newGroups[j];
|
|
288
|
+
newGroups[j] = temp;
|
|
289
|
+
|
|
290
|
+
transaction((tx) => {
|
|
291
|
+
this.groups.set(newGroups, tx);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
278
295
|
private _doSelectGroup(index: number) {
|
|
279
296
|
const group = this.getGroup(index);
|
|
280
297
|
transaction((tx) => {
|
package/src/browser/xterm.ts
CHANGED
|
@@ -177,13 +177,26 @@ export class XTerm extends Disposable implements IXTerm {
|
|
|
177
177
|
};
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
findNext(text: string) {
|
|
180
|
+
findNext(text: string, searchOptions: ISearchOptions = {}) {
|
|
181
181
|
const options: ISearchOptions = {
|
|
182
182
|
decorations: this.getFindColors(),
|
|
183
|
+
...searchOptions,
|
|
183
184
|
};
|
|
184
185
|
return this._searchAddon.findNext(text, options);
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
findPrevious(text: string, searchOptions: ISearchOptions = {}) {
|
|
189
|
+
const options: ISearchOptions = {
|
|
190
|
+
decorations: this.getFindColors(),
|
|
191
|
+
...searchOptions,
|
|
192
|
+
};
|
|
193
|
+
return this._searchAddon.findPrevious(text, options);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get onSearchResultsChange() {
|
|
197
|
+
return this._searchAddon.onDidChangeResults;
|
|
198
|
+
}
|
|
199
|
+
|
|
187
200
|
closeSearch() {
|
|
188
201
|
this._searchAddon.clearDecorations();
|
|
189
202
|
}
|
package/src/common/client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ISearchOptions } from '@xterm/addon-search';
|
|
2
|
+
import { IEvent, Terminal } from '@xterm/xterm';
|
|
2
3
|
|
|
3
4
|
import { Deferred, Disposable, Event, IDisposable } from '@opensumi/ide-core-common';
|
|
4
5
|
|
|
@@ -116,7 +117,11 @@ export interface ITerminalClient extends Disposable {
|
|
|
116
117
|
*
|
|
117
118
|
* @param text 用户输入的字符串
|
|
118
119
|
*/
|
|
119
|
-
findNext(text: string): void;
|
|
120
|
+
findNext(text: string, searchOptions?: ISearchOptions): void;
|
|
121
|
+
|
|
122
|
+
findPrevious(text: string, searchOptions?: ISearchOptions): void;
|
|
123
|
+
|
|
124
|
+
onSearchResultsChange: IEvent<{ resultIndex: number; resultCount: number }>;
|
|
120
125
|
|
|
121
126
|
closeSearch(): void;
|
|
122
127
|
|
package/src/common/controller.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IEvent } from '@xterm/xterm';
|
|
2
|
+
|
|
1
3
|
import { IContextKeyService } from '@opensumi/ide-core-browser';
|
|
2
4
|
import { Deferred, Disposable, Event, IDisposable, Uri } from '@opensumi/ide-core-common';
|
|
3
5
|
import { IObservable } from '@opensumi/ide-monaco/lib/common/observable';
|
|
@@ -113,10 +115,23 @@ export interface ITerminalController extends Disposable {
|
|
|
113
115
|
registerLinkProvider(provider: ITerminalExternalLinkProvider): IDisposable;
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
export interface IUIState {
|
|
119
|
+
isMatchCase: boolean;
|
|
120
|
+
isWholeWord: boolean;
|
|
121
|
+
isUseRegexp: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface ISearchResult {
|
|
125
|
+
resultCount: number;
|
|
126
|
+
resultIndex: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
export const ITerminalSearchService = Symbol('ITerminalSearchService');
|
|
117
130
|
export interface ITerminalSearchService {
|
|
118
131
|
isVisible: boolean;
|
|
119
132
|
onVisibleChange: Event<boolean>;
|
|
133
|
+
UIState: IUIState;
|
|
134
|
+
onResultChange: IEvent<ISearchResult> | undefined;
|
|
120
135
|
|
|
121
136
|
text: string;
|
|
122
137
|
|
|
@@ -124,6 +139,9 @@ export interface ITerminalSearchService {
|
|
|
124
139
|
clear(): void;
|
|
125
140
|
close(): void;
|
|
126
141
|
search(): void;
|
|
142
|
+
searchNext(): void;
|
|
143
|
+
searchPrevious(): void;
|
|
144
|
+
updateUIState(state: Partial<IUIState>): void;
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
export const ITerminalGroupViewService = Symbol('ITerminalGroupViewService');
|
|
@@ -140,6 +158,8 @@ export interface ITerminalGroupViewService {
|
|
|
140
158
|
selectGroup(index: number): void;
|
|
141
159
|
removeGroup(index: number): void;
|
|
142
160
|
|
|
161
|
+
swapGroup(i: number, j: number): void;
|
|
162
|
+
|
|
143
163
|
createWidget(group: IWidgetGroup, id?: string, reuse?: boolean, isSimpleWidget?: boolean): IWidget;
|
|
144
164
|
getWidget(id: string): IWidget;
|
|
145
165
|
selectWidget(widgetId: string): void;
|
package/src/common/pty.ts
CHANGED
|
@@ -539,7 +539,7 @@ export interface IShellLaunchConfig {
|
|
|
539
539
|
/**
|
|
540
540
|
* The color ID to use for this terminal. If not specified it will use the default fallback
|
|
541
541
|
*/
|
|
542
|
-
color?: vscode.ThemeColor;
|
|
542
|
+
color?: string | vscode.ThemeColor;
|
|
543
543
|
|
|
544
544
|
/**
|
|
545
545
|
* When a parent terminal is provided via API, the group needs
|
package/src/common/render.ts
CHANGED
|
@@ -18,8 +18,11 @@ export interface ItemProps {
|
|
|
18
18
|
onDropdown?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
19
19
|
onContextMenu?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
20
20
|
getKeybinding?: (command: string) => string;
|
|
21
|
+
onDrop?: (event: React.DragEvent) => void;
|
|
22
|
+
onDragStart?: (event: React.DragEvent) => void;
|
|
21
23
|
provider: ITerminalRenderProvider;
|
|
22
24
|
theme: ThemeType;
|
|
25
|
+
draggable?: boolean;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export const ITerminalRenderProvider = Symbol('TerminalRenderProvider');
|