Skip to content

Commit ebf5f76

Browse files
committed
Allow editing multiple lines of text in the search view - fix microsoft#62304
1 parent af821e2 commit ebf5f76

7 files changed

Lines changed: 93 additions & 13 deletions

File tree

src/vs/base/browser/ui/findinput/findInput.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
.monaco-findInput .monaco-inputbox {
1212
font-size: 13px;
1313
width: 100%;
14-
height: 25px;
1514
}
1615

1716
.monaco-findInput > .controls {

src/vs/base/browser/ui/findinput/findInput.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface IFindInputOptions extends IFindInputStyles {
2323
readonly width?: number;
2424
readonly validation?: IInputValidator;
2525
readonly label: string;
26+
readonly flexibleHeight?: boolean;
2627

2728
readonly appendCaseSensitiveLabel?: string;
2829
readonly appendWholeWordsLabel?: string;
@@ -118,7 +119,7 @@ export class FindInput extends Widget {
118119
this.domNode = null;
119120
this.inputBox = null;
120121

121-
this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history);
122+
this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history, options.flexibleHeight);
122123

123124
if (Boolean(parent)) {
124125
parent.appendChild(this.domNode);
@@ -287,7 +288,7 @@ export class FindInput extends Widget {
287288
this.inputBox.width = w;
288289
}
289290

290-
private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[]): void {
291+
private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[], flexibleHeight: boolean): void {
291292
this.domNode = document.createElement('div');
292293
this.domNode.style.width = this.width + 'px';
293294
dom.addClass(this.domNode, 'monaco-findInput');
@@ -310,7 +311,8 @@ export class FindInput extends Widget {
310311
inputValidationErrorBackground: this.inputValidationErrorBackground,
311312
inputValidationErrorForeground: this.inputValidationErrorForeground,
312313
inputValidationErrorBorder: this.inputValidationErrorBorder,
313-
history
314+
history,
315+
flexibleHeight
314316
}));
315317

316318
this.regex = this._register(new RegexCheckbox({

src/vs/base/browser/ui/inputbox/inputBox.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ export class InputBox extends Widget {
291291

292292
public set width(width: number) {
293293
this.input.style.width = width + 'px';
294+
if (this.mirror) {
295+
this.mirror.style.width = width + 'px';
296+
}
294297
}
295298

296299
public showMessage(message: IMessage, force?: boolean): void {

src/vs/workbench/parts/search/browser/media/searchview.css

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,32 @@
2929
margin-left: 17px;
3030
}
3131

32-
.search-view .search-widget .input-box,
33-
.search-view .search-widget .input-box .monaco-inputbox {
34-
height: 25px;
32+
.search-view .search-widget .monaco-inputbox > .wrapper {
33+
height: 100%;
34+
}
35+
36+
.search-view .search-widget .monaco-inputbox > .wrapper > .mirror {
37+
max-height: 134px;
38+
}
39+
40+
.search-view .monaco-inputbox > .wrapper > textarea.input {
41+
overflow: initial;
42+
height: 26px; /* set initial height before measure */
43+
}
44+
45+
.search-view .monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar {
46+
display: none;
3547
}
3648

3749
.search-view .search-widget .monaco-findInput {
3850
display: inline-block;
3951
vertical-align: middle;
4052
}
4153

54+
.search-view .search-widget .monaco-findInput > .controls {
55+
top: 4px; /* Adjust controls, search view input box is slighty taller than find widget due to measurement */
56+
}
57+
4258
.search-view .search-widget .replace-container {
4359
margin-top: 6px;
4460
position: relative;

src/vs/workbench/parts/search/browser/searchWidget.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ export class SearchWidget extends Widget {
281281
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keyBindingService),
282282
appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keyBindingService),
283283
appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleRegexCommandId), this.keyBindingService),
284-
history: options.searchHistory
284+
history: options.searchHistory,
285+
flexibleHeight: true
285286
};
286287

287288
let searchInputContainer = dom.append(parent, dom.$('.search-container.input-box'));
@@ -329,7 +330,8 @@ export class SearchWidget extends Widget {
329330
this.replaceInput = this._register(new ContextScopedHistoryInputBox(replaceBox, this.contextViewService, {
330331
ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'),
331332
placeholder: nls.localize('search.replace.placeHolder', "Replace"),
332-
history: options.replaceHistory || []
333+
history: options.replaceHistory || [],
334+
flexibleHeight: true
333335
}, this.contextKeyService));
334336
this._register(attachInputBoxStyler(this.replaceInput, this.themeService));
335337
this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent));
@@ -376,6 +378,7 @@ export class SearchWidget extends Widget {
376378
if (currentState !== newState) {
377379
this.replaceActive.set(newState);
378380
this._onReplaceStateChange.fire(newState);
381+
this.replaceInput.layout();
379382
}
380383
}
381384

@@ -429,6 +432,20 @@ export class SearchWidget extends Widget {
429432
}
430433
keyboardEvent.preventDefault();
431434
}
435+
436+
else if (keyboardEvent.equals(KeyCode.UpArrow)) {
437+
const ta = this.searchInput.domNode.querySelector('textarea');
438+
if (ta && ta.selectionStart > 0) {
439+
keyboardEvent.stopPropagation();
440+
}
441+
}
442+
443+
else if (keyboardEvent.equals(KeyCode.DownArrow)) {
444+
const ta = this.searchInput.domNode.querySelector('textarea');
445+
if (ta && ta.selectionEnd < ta.value.length) {
446+
keyboardEvent.stopPropagation();
447+
}
448+
}
432449
}
433450

434451
private onCaseSensitiveKeyDown(keyboardEvent: IKeyboardEvent) {
@@ -466,6 +483,20 @@ export class SearchWidget extends Widget {
466483
this.searchInput.focus();
467484
keyboardEvent.preventDefault();
468485
}
486+
487+
else if (keyboardEvent.equals(KeyCode.UpArrow)) {
488+
const ta = this.searchInput.domNode.querySelector('textarea');
489+
if (ta && ta.selectionStart > 0) {
490+
keyboardEvent.stopPropagation();
491+
}
492+
}
493+
494+
else if (keyboardEvent.equals(KeyCode.DownArrow)) {
495+
const ta = this.searchInput.domNode.querySelector('textarea');
496+
if (ta && ta.selectionEnd < ta.value.length) {
497+
keyboardEvent.stopPropagation();
498+
}
499+
}
469500
}
470501

471502
private onReplaceActionbarKeyDown(keyboardEvent: IKeyboardEvent) {

src/vs/workbench/parts/search/common/queryBuilder.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,8 @@ export class QueryBuilder {
6767
) { }
6868

6969
text(contentPattern: IPatternInfo, folderResources?: uri[], options: ITextQueryBuilderOptions = {}): ITextQuery {
70-
contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern);
71-
contentPattern.isMultiline = this.isMultiline(contentPattern);
70+
contentPattern = this.getContentPattern(contentPattern);
7271
const searchConfig = this.configurationService.getValue<ISearchConfiguration>();
73-
contentPattern.wordSeparators = searchConfig.editor.wordSeparators;
7472

7573
const fallbackToPCRE = folderResources && folderResources.some(folder => {
7674
const folderConfig = this.configurationService.getValue<ISearchConfiguration>({ resource: folder });
@@ -91,6 +89,28 @@ export class QueryBuilder {
9189
};
9290
}
9391

92+
/**
93+
* Adjusts input pattern for config
94+
*/
95+
private getContentPattern(inputPattern: IPatternInfo): IPatternInfo {
96+
const searchConfig = this.configurationService.getValue<ISearchConfiguration>();
97+
98+
const newPattern = {
99+
...inputPattern,
100+
wordSeparators: searchConfig.editor.wordSeparators
101+
};
102+
103+
if (this.isCaseSensitive(inputPattern)) {
104+
newPattern.isCaseSensitive = true;
105+
}
106+
107+
if (this.isMultiline(inputPattern)) {
108+
newPattern.isMultiline = true;
109+
}
110+
111+
return newPattern;
112+
}
113+
94114
file(folderResources: uri[] | undefined, options: IFileQueryBuilderOptions = {}): IFileQuery {
95115
const commonQuery = this.commonQuery(folderResources, options);
96116
return <IFileQuery>{
@@ -165,6 +185,10 @@ export class QueryBuilder {
165185
return true;
166186
}
167187

188+
if (contentPattern.pattern.indexOf('\n') >= 0) {
189+
return true;
190+
}
191+
168192
return false;
169193
}
170194

src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as cp from 'child_process';
77
import { EventEmitter } from 'events';
88
import * as path from 'path';
99
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
10-
import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM } from 'vs/base/common/strings';
10+
import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM, escapeRegExpCharacters } from 'vs/base/common/strings';
1111
import { URI } from 'vs/base/common/uri';
1212
import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/platform/search/common/search';
1313
import * as vscode from 'vscode';
@@ -343,6 +343,11 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti
343343
pattern = '\\-\\-';
344344
}
345345

346+
if (query.isMultiline && !query.isRegExp) {
347+
query.pattern = escapeRegExpCharacters(query.pattern);
348+
query.isRegExp = true;
349+
}
350+
346351
if ((<IExtendedExtensionSearchOptions>options).usePCRE2) {
347352
args.push('--pcre2');
348353

0 commit comments

Comments
 (0)