Skip to content

Commit 0c22a33

Browse files
Tyriarmeganrogge
andauthored
Allow opt-in multi-line quick fixes (microsoft#162189)
Fixes microsoft#161997 Co-authored-by: meganrogge <[email protected]>
1 parent 236adc2 commit 0c22a33

File tree

5 files changed

+53
-52
lines changed

5 files changed

+53
-52
lines changed

src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
490490
commandStartLineContent: this._currentCommand.commandStartLineContent,
491491
hasOutput: () => !executedMarker?.isDisposed && !endMarker?.isDisposed && !!(executedMarker && endMarker && executedMarker?.line < endMarker!.line),
492492
getOutput: () => getOutputForCommand(executedMarker, endMarker, buffer),
493-
getOutputMatch: (outputMatcher?: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher),
493+
getOutputMatch: (outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }) => getOutputMatchForCommand(executedMarker, endMarker, buffer, this._terminal.cols, outputMatcher),
494494
markProperties: options?.markProperties
495495
};
496496
this._commands.push(newCommand);
@@ -647,43 +647,35 @@ function getOutputForCommand(executedMarker: IMarker | undefined, endMarker: IMa
647647
return output === '' ? undefined : output;
648648
}
649649

650-
export function getOutputMatchForCommand(executedMarker: IMarker | undefined, endMarker: IMarker | undefined, buffer: IBuffer, cols: number, outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number } | undefined): RegExpMatchArray | undefined {
650+
export function getOutputMatchForCommand(executedMarker: IMarker | undefined, endMarker: IMarker | undefined, buffer: IBuffer, cols: number, outputMatcher: { lineMatcher: string | RegExp; anchor?: 'top' | 'bottom'; offset?: number; length?: number }): RegExpMatchArray | undefined {
651651
if (!executedMarker || !endMarker) {
652652
return undefined;
653653
}
654654
const startLine = executedMarker.line;
655655
const endLine = endMarker.line;
656656

657-
if (startLine === endLine) {
658-
return undefined;
659-
}
660-
661-
let line: string | undefined;
662-
if (outputMatcher?.anchor === 'bottom') {
657+
const matcher = outputMatcher.lineMatcher;
658+
const linesToCheck = typeof matcher === 'string' ? 1 : countNewLines(matcher);
659+
const lines: string[] = [];
660+
if (outputMatcher.anchor === 'bottom') {
663661
for (let i = endLine - (outputMatcher.offset || 0); i >= startLine; i--) {
664-
let wrappedLineStart = i;
665-
const wrappedLineEnd = i;
666-
while (wrappedLineStart >= startLine && buffer.getLine(wrappedLineStart)?.isWrapped) {
667-
wrappedLineStart--;
662+
lines.unshift(getXtermLineContent(buffer, i, i, cols));
663+
if (lines.length > linesToCheck) {
664+
lines.pop();
668665
}
669-
i = wrappedLineStart;
670-
line = getXtermLineContent(buffer, wrappedLineStart, wrappedLineEnd, cols);
671-
const match = line.match(outputMatcher.lineMatcher);
666+
const match = lines.join('\n').match(matcher);
672667
if (match) {
673668
return match;
674669
}
675670
}
676671
} else {
677-
for (let i = startLine + (outputMatcher?.offset || 0); i < endLine; i++) {
678-
const wrappedLineStart = i;
679-
let wrappedLineEnd = i;
680-
while (wrappedLineEnd + 1 < endLine && buffer.getLine(wrappedLineEnd + 1)?.isWrapped) {
681-
wrappedLineEnd++;
672+
for (let i = startLine + (outputMatcher.offset || 0); i < endLine; i++) {
673+
lines.push(getXtermLineContent(buffer, i, i, cols));
674+
if (lines.length === linesToCheck) {
675+
lines.shift();
682676
}
683-
i = wrappedLineEnd;
684-
line = getXtermLineContent(buffer, wrappedLineStart, wrappedLineEnd, cols);
685677
if (outputMatcher) {
686-
const match = line.match(outputMatcher.lineMatcher);
678+
const match = lines.join('\n').match(matcher);
687679
if (match) {
688680
return match;
689681
}
@@ -709,3 +701,17 @@ function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number
709701
}
710702
return content;
711703
}
704+
705+
function countNewLines(regex: RegExp): number {
706+
if (!regex.multiline) {
707+
return 1;
708+
}
709+
const source = regex.source;
710+
let count = 1;
711+
let i = source.indexOf('\\n');
712+
while (i !== -1) {
713+
count++;
714+
i = source.indexOf('\\n', i + 1);
715+
}
716+
return count;
717+
}

src/vs/workbench/contrib/terminal/browser/terminal.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { IEditableData } from 'vs/workbench/common/views';
2020
import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget';
2121
import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList';
2222
import { ITerminalQuickFix } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon';
23-
import { INavigationMode, IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalBackend, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
23+
import { INavigationMode, IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalBackend, ITerminalConfigHelper, ITerminalFont, ITerminalOutputMatcher, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
2424
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
2525
import { IMarker } from 'xterm';
2626

@@ -933,29 +933,6 @@ export interface ITerminalQuickFixAction extends IAction {
933933
addNewLine?: boolean;
934934
}
935935

936-
/**
937-
* A matcher that runs on a sub-section of a terminal command's output
938-
*/
939-
export interface ITerminalOutputMatcher {
940-
/**
941-
* A string or regex to match against the unwrapped line.
942-
*/
943-
lineMatcher: string | RegExp;
944-
/**
945-
* Which side of the output to anchor the {@link offset} and {@link length} against.
946-
*/
947-
anchor: 'top' | 'bottom';
948-
/**
949-
* How far from either the top or the bottom of the butter to start matching against.
950-
*/
951-
offset: number;
952-
/**
953-
* The number of rows to match against, this should be as small as possible for performance
954-
* reasons.
955-
*/
956-
length: number;
957-
}
958-
959936
export interface IXtermTerminal {
960937
/**
961938
* An object that tracks when commands are run and enables navigating and selecting between

src/vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ITerminalCommand } from 'vs/workbench/contrib/terminal/common/terminal'
1212
export const GitCommandLineRegex = /git/;
1313
export const GitPushCommandLineRegex = /git\s+push/;
1414
export const AnyCommandLineRegex = /.+/;
15-
export const GitSimilarOutputRegex = /most similar command is\s*([^\s]{3,})/;
15+
export const GitSimilarOutputRegex = /most similar command is\s*\n\s*([^\s]{3,})/m;
1616
export const FreePortOutputRegex = /address already in use \d+\.\d+\.\d+\.\d+:(\d{4,5})|Unable to bind [^ ]*:(\d{4,5})|can't listen on port (\d{4,5})|listen EADDRINUSE [^ ]*:(\d{4,5})/;
1717
export const GitPushOutputRegex = /git push --set-upstream origin ([^\s]+)/;
1818
// The previous line starts with "Create a pull request for \'([^\s]+)\' on GitHub by visiting:\s*",

src/vs/workbench/contrib/terminal/common/terminal.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,28 @@ export interface IShellLaunchConfigResolveOptions {
9696
allowAutomationShell?: boolean;
9797
}
9898

99+
/**
100+
* A matcher that runs on a sub-section of a terminal command's output
101+
*/
99102
export interface ITerminalOutputMatcher {
103+
/**
104+
* A string or regex to match against the unwrapped line. If this is a regex with the multiline
105+
* flag, it will scan an amount of lines equal to `\n` instances in the regex + 1.
106+
*/
100107
lineMatcher: string | RegExp;
101-
anchor?: 'top' | 'bottom';
102-
offset?: number;
103-
length?: number;
108+
/**
109+
* Which side of the output to anchor the {@link offset} and {@link length} against.
110+
*/
111+
anchor: 'top' | 'bottom';
112+
/**
113+
* How far from either the top or the bottom of the butter to start matching against.
114+
*/
115+
offset: number;
116+
/**
117+
* The number of rows to match against, this should be as small as possible for performance
118+
* reasons.
119+
*/
120+
length: number;
104121
}
105122

106123
export interface ITerminalBackend {

src/vs/workbench/contrib/terminal/test/browser/quickFixAddon.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
1414
import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
1515
import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability';
1616
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
17-
import { ITerminalQuickFixAction, ITerminalInstance, ITerminalOutputMatcher } from 'vs/workbench/contrib/terminal/browser/terminal';
17+
import { ITerminalQuickFixAction, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
1818
import { freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutputRegex, GitPushOutputRegex, gitPushSetUpstream, gitSimilarCommand, GitSimilarOutputRegex } from 'vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions';
1919
import { TerminalQuickFixAddon, getQuickFixes } from 'vs/workbench/contrib/terminal/browser/xterm/quickFixAddon';
20+
import { ITerminalOutputMatcher } from 'vs/workbench/contrib/terminal/common/terminal';
2021
import { Terminal } from 'xterm';
2122

2223
suite('QuickFixAddon', () => {

0 commit comments

Comments
 (0)