Skip to content

Commit e037c13

Browse files
committed
correct bisecting of scroll posmap
1 parent e683292 commit e037c13

1 file changed

Lines changed: 45 additions & 23 deletions

File tree

ReText/syncscroll.py

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# You should have received a copy of the GNU General Public License
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17+
from bisect import bisect_left
18+
1719
from PyQt6.QtCore import QPoint
1820

1921

@@ -41,6 +43,11 @@ def __init__(self, previewFrame,
4143
self._updating_preview = False
4244
self._updating_editor = False
4345

46+
# Cached orderings for mapping between preview positions and source lines
47+
self._posmap_lines = []
48+
self._preview_posmap = []
49+
self._preview_positions = []
50+
4451
self.frame.contentsSizeChanged.connect(self._handlePreviewResized)
4552
self.frame.loadStarted.connect(self._handleLoadStarted)
4653
self.frame.loadFinished.connect(self._handleLoadFinished)
@@ -115,11 +122,11 @@ def _updatePreviewScrollPosition(self):
115122
# Do a binary search through the posmap to find the nearest line above
116123
# and below the line to scroll to for which the rendered position is
117124
# known.
118-
posmap_lines = [0] + sorted(self.posmap.keys())
125+
posmap_lines = self._posmap_lines
119126
min_index = 0
120127
max_index = len(posmap_lines) - 1
121128
while max_index - min_index > 1:
122-
current_index = int((min_index + max_index) // 2)
129+
current_index = (min_index + max_index) // 2
123130
if posmap_lines[current_index] > line_to_scroll_to:
124131
max_index = current_index
125132
else:
@@ -159,6 +166,15 @@ def _setPositionMap(self, posmap):
159166
self.posmap = posmap
160167
if posmap:
161168
self.posmap[0] = 0
169+
if self.posmap:
170+
self._posmap_lines = sorted(self.posmap.keys())
171+
preview_sorted = sorted(self.posmap.items(), key=lambda item: item[1])
172+
self._preview_posmap = [(preview, line) for line, preview in preview_sorted]
173+
self._preview_positions = [preview for preview, _ in self._preview_posmap]
174+
else:
175+
self._posmap_lines = []
176+
self._preview_posmap = []
177+
self._preview_positions = []
162178

163179
def handlePreviewScrolled(self, previewScrollPosition):
164180
"""
@@ -180,34 +196,40 @@ def handlePreviewScrolled(self, previewScrollPosition):
180196
except AttributeError:
181197
preview_y = float(previewScrollPosition)
182198

183-
# Binary search using line numbers to find nearest posmap values
184-
posmap_lines = [0] + sorted(self.posmap.keys())
185-
min_index = 0
186-
max_index = len(posmap_lines) - 1
187-
while max_index - min_index > 1:
188-
current_index = int((min_index + max_index) // 2)
189-
current_line = posmap_lines[current_index]
190-
if self.posmap[current_line] > preview_y:
191-
max_index = current_index
192-
else:
193-
min_index = current_index
199+
if not self._preview_posmap:
200+
return
194201

195-
min_line = posmap_lines[min_index]
196-
max_line = posmap_lines[max_index]
202+
if len(self._preview_posmap) == 1:
203+
_, line = self._preview_posmap[0]
204+
editor_scroll_to = self.sourceLineToEditorPosition(line)
205+
else:
206+
index = bisect_left(self._preview_positions, preview_y)
207+
if index <= 0:
208+
min_preview_pos, min_line = self._preview_posmap[0]
209+
max_preview_pos, max_line = self._preview_posmap[1]
210+
elif index >= len(self._preview_posmap):
211+
min_preview_pos, min_line = self._preview_posmap[-2]
212+
max_preview_pos, max_line = self._preview_posmap[-1]
213+
else:
214+
min_preview_pos, min_line = self._preview_posmap[index - 1]
215+
max_preview_pos, max_line = self._preview_posmap[index]
197216

198-
min_preview_pos = self.posmap[min_line]
199-
max_preview_pos = self.posmap[max_line]
217+
min_textedit_pos = self.sourceLineToEditorPosition(min_line)
218+
max_textedit_pos = self.sourceLineToEditorPosition(max_line)
200219

201-
min_textedit_pos = self.sourceLineToEditorPosition(min_line)
202-
max_textedit_pos = self.sourceLineToEditorPosition(max_line)
220+
editor_scroll_to = self._linearScale(
221+
preview_y,
222+
min_preview_pos,
223+
max_preview_pos,
224+
min_textedit_pos,
225+
max_textedit_pos,
226+
)
203227

204-
editor_scroll_to = self._linearScale(preview_y,
205-
min_preview_pos, max_preview_pos,
206-
min_textedit_pos, max_textedit_pos)
228+
editor_scroll_value = int(editor_scroll_to)
207229

208230
# Apply editor scroll with guard to avoid triggering editor→preview update
209231
self._updating_editor = True
210232
try:
211-
self._setEditorScrollValue(int(editor_scroll_to))
233+
self._setEditorScrollValue(editor_scroll_value)
212234
finally:
213235
self._updating_editor = False

0 commit comments

Comments
 (0)