|
7 | 7 | TextEditor, |
8 | 8 | next, |
9 | 9 | } from "@cursorless/common"; |
| 10 | +import { zip } from "itertools"; |
10 | 11 | import { ScopeHandlerFactory } from "."; |
| 12 | +import { Target } from "../../../typings/target.types"; |
11 | 13 | import { createContinuousRangeTarget } from "../../createContinuousRangeTarget"; |
12 | 14 | import { BaseScopeHandler } from "./BaseScopeHandler"; |
13 | 15 | import type { TargetScope } from "./scope.types"; |
@@ -85,24 +87,23 @@ function combineScopes(scope1: TargetScope, scope2: TargetScope): TargetScope { |
85 | 87 | editor: scope1.editor, |
86 | 88 | domain: scope1.domain.union(scope2.domain), |
87 | 89 | getTargets: (isReversed) => { |
88 | | - const target1 = scope1.getTargets(isReversed)[0]; |
89 | | - const target2 = scope2.getTargets(isReversed)[0]; |
90 | | - |
91 | | - const [startTarget, endTarget] = target1.contentRange.start.isBefore( |
92 | | - target2.contentRange.start, |
93 | | - ) |
94 | | - ? [target1, target2] |
95 | | - : [target2, target1]; |
96 | | - |
97 | | - return [ |
98 | | - createContinuousRangeTarget( |
| 90 | + return zip( |
| 91 | + scope1.getTargets(isReversed), |
| 92 | + scope2.getTargets(isReversed), |
| 93 | + ).map(([target1, target2]) => { |
| 94 | + const [startTarget, endTarget] = getTargetsInDocumentOrder( |
| 95 | + target1, |
| 96 | + target2, |
| 97 | + ); |
| 98 | + |
| 99 | + return createContinuousRangeTarget( |
99 | 100 | isReversed, |
100 | 101 | startTarget, |
101 | 102 | endTarget, |
102 | 103 | true, |
103 | 104 | true, |
104 | | - ), |
105 | | - ]; |
| 105 | + ); |
| 106 | + }); |
106 | 107 | }, |
107 | 108 | }; |
108 | 109 | } |
@@ -145,34 +146,41 @@ function isAdjacent(scope1: TargetScope, scope2: TargetScope): boolean { |
145 | 146 | return true; |
146 | 147 | } |
147 | 148 |
|
148 | | - const target1 = scope1.getTargets(false)[0]; |
149 | | - const target2 = scope2.getTargets(false)[0]; |
150 | | - |
151 | | - const [leadingTarget, trailingTarget] = target1.contentRange.start.isBefore( |
152 | | - target2.contentRange.start, |
153 | | - ) |
154 | | - ? [target1, target2] |
155 | | - : [target2, target1]; |
| 149 | + const [startTarget, endTarget] = getTargetsInDocumentOrder( |
| 150 | + scope1.getTargets(false)[0], |
| 151 | + scope2.getTargets(false)[0], |
| 152 | + ); |
156 | 153 |
|
157 | 154 | const leadingRange = |
158 | | - leadingTarget.getTrailingDelimiterTarget()?.contentRange ?? |
159 | | - leadingTarget.contentRange; |
| 155 | + startTarget.getTrailingDelimiterTarget()?.contentRange ?? |
| 156 | + startTarget.contentRange; |
160 | 157 | const trailingRange = |
161 | | - trailingTarget.getLeadingDelimiterTarget()?.contentRange ?? |
162 | | - trailingTarget.contentRange; |
| 158 | + endTarget.getLeadingDelimiterTarget()?.contentRange ?? |
| 159 | + endTarget.contentRange; |
163 | 160 |
|
164 | 161 | if (leadingRange.intersection(trailingRange) != null) { |
165 | 162 | return true; |
166 | 163 | } |
167 | 164 |
|
| 165 | + // Non line targets are excluded if they are separated by more than one line |
168 | 166 | if ( |
169 | | - !leadingTarget.isLine && |
| 167 | + !startTarget.isLine && |
170 | 168 | trailingRange.start.line - leadingRange.end.line > 1 |
171 | 169 | ) { |
172 | 170 | return false; |
173 | 171 | } |
174 | 172 |
|
| 173 | + // Finally targets are excluded if there is non whitespace text between them |
175 | 174 | const rangeBetween = new Range(leadingRange.end, trailingRange.start); |
176 | | - const text = leadingTarget.editor.document.getText(rangeBetween); |
| 175 | + const text = startTarget.editor.document.getText(rangeBetween); |
177 | 176 | return /^\s*$/.test(text); |
178 | 177 | } |
| 178 | + |
| 179 | +function getTargetsInDocumentOrder( |
| 180 | + target1: Target, |
| 181 | + target2: Target, |
| 182 | +): [Target, Target] { |
| 183 | + return target1.contentRange.start.isBefore(target2.contentRange.start) |
| 184 | + ? [target1, target2] |
| 185 | + : [target2, target1]; |
| 186 | +} |
0 commit comments