Skip to content

Updated regex stage with support for text range and multiple line pat… #959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 1, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 65 additions & 43 deletions src/processTargets/modifiers/scopeTypeStages/RegexStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,21 @@ class RegexStageBase implements ModifierStage {
}

private getEveryTarget(target: Target): Target[] {
const { contentRange, editor } = target;
const start = target.hasExplicitRange
? contentRange.start
: editor.document.lineAt(contentRange.start).range.start;
const end = target.hasExplicitRange
? contentRange.end
: editor.document.lineAt(contentRange.end).range.end;
const targets: Target[] = [];

for (let i = start.line; i <= end.line; ++i) {
this.getMatchesForLine(editor, i).forEach((range) => {
// Regex match and selection intersects
if (
range.end.isAfterOrEqual(start) &&
range.start.isBeforeOrEqual(end)
) {
targets.push(this.getTargetFromRange(target, range));
}
});
}
const { editor, contentRange } = target;

const searchRange = new Range(
this.expandRangeForSearch(target.editor, contentRange.start).start,
this.expandRangeForSearch(target.editor, contentRange.end).end
);

const matches = this.getMatchesInRange(editor, searchRange);
const targets = (
target.hasExplicitRange
? matches.filter((match) => match.intersection(contentRange) != null)
: matches
).map((contentRange) =>
this.rangeToTarget(target.isReversed, target.editor, contentRange)
);

if (targets.length === 0) {
throw new NoContainingScopeError(this.modifier.scopeType.type);
Expand All @@ -52,47 +47,74 @@ class RegexStageBase implements ModifierStage {
}

private getSingleTarget(target: Target): Target {
const { editor } = target;
const start = this.getMatchForPos(editor, target.contentRange.start).start;
const end = this.getMatchForPos(editor, target.contentRange.end).end;
const contentRange = new Range(start, end);
return this.getTargetFromRange(target, contentRange);
}

private getTargetFromRange(target: Target, contentRange: Range): Target {
return new TokenTarget({
editor: target.editor,
isReversed: target.isReversed,
contentRange,
});
const { editor, isReversed, contentRange } = target;

return this.rangeToTarget(
isReversed,
editor,
this.getMatchContainingPosition(editor, contentRange.start).union(
this.getMatchContainingPosition(editor, contentRange.end)
)
);
}

private getMatchForPos(editor: TextEditor, position: Position) {
const match = this.getMatchesForLine(editor, position.line).find((range) =>
range.contains(position)
private getMatchContainingPosition(
editor: TextEditor,
position: Position
): Range {
const textRange = this.expandRangeForSearch(editor, position);
const match = this.getMatchesInRange(editor, textRange).find(
(contentRange) => contentRange.contains(position)
);
if (match == null) {
throw new NoContainingScopeError(this.modifier.scopeType.type);
}
return match;
}

private getMatchesForLine(editor: TextEditor, lineNum: number) {
const line = editor.document.lineAt(lineNum);
const result = [...line.text.matchAll(this.regex)].map(
/**
* Constructs a range from {@link position} within which to search for
* instances of {@link regex}. By default we expand to containing line, as
* all our regexes today operate within a line, but deriving modifier stages
* can override this to properly handle multiline regexes.
* @param editor The editor containing {@link position}
* @param position The position from which to expand for searching
* @returns A range within which to search for instances of {@link regex}
*/
protected expandRangeForSearch(
editor: TextEditor,
position: Position
): Range {
return editor.document.lineAt(position.line).range;
}

private getMatchesInRange(editor: TextEditor, range: Range): Range[] {
const offset = editor.document.offsetAt(range.start);
const text = editor.document.getText(range);
const result = [...text.matchAll(this.regex)].map(
(match) =>
new Range(
lineNum,
match.index!,
lineNum,
match.index! + match[0].length
editor.document.positionAt(offset + match.index!),
editor.document.positionAt(offset + match.index! + match[0].length)
)
);
if (result == null) {
throw new NoContainingScopeError(this.modifier.scopeType.type);
}
return result;
}

private rangeToTarget(
isReversed: boolean,
editor: TextEditor,
contentRange: Range
): Target {
return new TokenTarget({
editor,
isReversed,
contentRange,
});
}
}

export class NonWhitespaceSequenceStage extends RegexStageBase {
Expand Down