@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.
Files changed (97) hide show
  1. package/lib/browser/component/resize.view.d.ts.map +1 -1
  2. package/lib/browser/component/resize.view.js +2 -2
  3. package/lib/browser/component/resize.view.js.map +1 -1
  4. package/lib/browser/component/search.module.less +61 -0
  5. package/lib/browser/component/search.view.d.ts +3 -0
  6. package/lib/browser/component/search.view.d.ts.map +1 -0
  7. package/lib/browser/component/search.view.js +89 -0
  8. package/lib/browser/component/search.view.js.map +1 -0
  9. package/lib/browser/component/tab.item.d.ts.map +1 -1
  10. package/lib/browser/component/tab.item.js +23 -1
  11. package/lib/browser/component/tab.item.js.map +1 -1
  12. package/lib/browser/component/tab.view.d.ts.map +1 -1
  13. package/lib/browser/component/tab.view.js +10 -1
  14. package/lib/browser/component/tab.view.js.map +1 -1
  15. package/lib/browser/component/terminal.module.less +3 -27
  16. package/lib/browser/component/terminal.view.d.ts.map +1 -1
  17. package/lib/browser/component/terminal.view.js +2 -32
  18. package/lib/browser/component/terminal.view.js.map +1 -1
  19. package/lib/browser/component/terminal.widget.d.ts.map +1 -1
  20. package/lib/browser/component/terminal.widget.js +9 -3
  21. package/lib/browser/component/terminal.widget.js.map +1 -1
  22. package/lib/browser/contribution/terminal.view.d.ts.map +1 -1
  23. package/lib/browser/contribution/terminal.view.js +2 -0
  24. package/lib/browser/contribution/terminal.view.js.map +1 -1
  25. package/lib/browser/links/link-manager.d.ts.map +1 -1
  26. package/lib/browser/links/link-manager.js +10 -7
  27. package/lib/browser/links/link-manager.js.map +1 -1
  28. package/lib/browser/links/validated-local-link-provider.d.ts +1 -0
  29. package/lib/browser/links/validated-local-link-provider.d.ts.map +1 -1
  30. package/lib/browser/links/validated-local-link-provider.js +55 -42
  31. package/lib/browser/links/validated-local-link-provider.js.map +1 -1
  32. package/lib/browser/links/word-link-provider.d.ts +29 -0
  33. package/lib/browser/links/word-link-provider.d.ts.map +1 -0
  34. package/lib/browser/links/word-link-provider.js +153 -0
  35. package/lib/browser/links/word-link-provider.js.map +1 -0
  36. package/lib/browser/terminal.client.d.ts +7 -1
  37. package/lib/browser/terminal.client.d.ts.map +1 -1
  38. package/lib/browser/terminal.client.js +9 -2
  39. package/lib/browser/terminal.client.js.map +1 -1
  40. package/lib/browser/terminal.controller.d.ts +1 -2
  41. package/lib/browser/terminal.controller.d.ts.map +1 -1
  42. package/lib/browser/terminal.controller.js +6 -6
  43. package/lib/browser/terminal.controller.js.map +1 -1
  44. package/lib/browser/terminal.hover.manager.d.ts.map +1 -1
  45. package/lib/browser/terminal.hover.manager.js +4 -2
  46. package/lib/browser/terminal.hover.manager.js.map +1 -1
  47. package/lib/browser/terminal.search.d.ts +9 -1
  48. package/lib/browser/terminal.search.d.ts.map +1 -1
  49. package/lib/browser/terminal.search.js +49 -1
  50. package/lib/browser/terminal.search.js.map +1 -1
  51. package/lib/browser/terminal.view.d.ts +1 -0
  52. package/lib/browser/terminal.view.d.ts.map +1 -1
  53. package/lib/browser/terminal.view.js +13 -0
  54. package/lib/browser/terminal.view.js.map +1 -1
  55. package/lib/browser/xterm.d.ts +7 -1
  56. package/lib/browser/xterm.d.ts.map +1 -1
  57. package/lib/browser/xterm.js +12 -1
  58. package/lib/browser/xterm.js.map +1 -1
  59. package/lib/common/client.d.ts +8 -2
  60. package/lib/common/client.d.ts.map +1 -1
  61. package/lib/common/client.js.map +1 -1
  62. package/lib/common/controller.d.ts +16 -0
  63. package/lib/common/controller.d.ts.map +1 -1
  64. package/lib/common/controller.js.map +1 -1
  65. package/lib/common/pty.d.ts +1 -1
  66. package/lib/common/pty.d.ts.map +1 -1
  67. package/lib/common/render.d.ts +3 -0
  68. package/lib/common/render.d.ts.map +1 -1
  69. package/lib/common/render.js.map +1 -1
  70. package/lib/common/xterm.d.ts +8 -2
  71. package/lib/common/xterm.d.ts.map +1 -1
  72. package/lib/common/xterm.js.map +1 -1
  73. package/package.json +18 -18
  74. package/src/browser/component/resize.view.tsx +2 -1
  75. package/src/browser/component/search.module.less +61 -0
  76. package/src/browser/component/search.view.tsx +142 -0
  77. package/src/browser/component/tab.item.tsx +38 -0
  78. package/src/browser/component/tab.view.tsx +12 -0
  79. package/src/browser/component/terminal.module.less +3 -27
  80. package/src/browser/component/terminal.view.tsx +3 -56
  81. package/src/browser/component/terminal.widget.tsx +8 -3
  82. package/src/browser/contribution/terminal.view.ts +2 -0
  83. package/src/browser/links/link-manager.ts +11 -8
  84. package/src/browser/links/validated-local-link-provider.ts +68 -48
  85. package/src/browser/links/word-link-provider.ts +175 -0
  86. package/src/browser/terminal.client.ts +13 -2
  87. package/src/browser/terminal.controller.ts +7 -8
  88. package/src/browser/terminal.hover.manager.ts +4 -4
  89. package/src/browser/terminal.search.ts +49 -2
  90. package/src/browser/terminal.view.ts +17 -0
  91. package/src/browser/xterm.ts +14 -1
  92. package/src/common/client.ts +7 -2
  93. package/src/common/controller.ts +20 -0
  94. package/src/common/pty.ts +1 -1
  95. package/src/common/render.ts +3 -0
  96. package/src/common/xterm-private.d.ts +7 -4
  97. 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.actualCellWidth,
169
- height: core._renderService.dimensions.actualCellHeight,
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(linkUrl);
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
- // Convert the link text's string index into a wrapped buffer range
174
- const bufferRange = convertLinkRangeToBuffer(
175
- lines,
176
- this._xterm.cols,
177
- {
178
- startColumn: stringIndex + 1,
179
- startLineNumber: 1,
180
- endColumn: stringIndex + link.length + 1,
181
- endLineNumber: 1,
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.findNext(text);
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
- if (!this.terminalContextKey) {
401
- this.terminalContextKey = this.injector.get(TerminalContextKey, [dom]);
402
- this.terminalContextKey.isTerminalFocused.set(this._focus);
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
- const overlayContainer = document.querySelector('#ide-overlay');
17
+ let overlayContainer = document.querySelector('#terminal-link-hover-overlay');
18
18
 
19
19
  if (!overlayContainer) {
20
- throw new Error('ide-overlay is requried');
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 { ITerminalClient, ITerminalController, ITerminalGroupViewService, ITerminalSearchService } from '../common';
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) => {
@@ -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
  }
@@ -1,4 +1,5 @@
1
- import { Terminal } from '@xterm/xterm';
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
 
@@ -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
@@ -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');