@@ -589,6 +589,21 @@ pub const LineIndex = struct {
589589 }
590590 }
591591
592+ /// line_starts内でtargetより大きい最初のインデックスを返す(upper bound)
593+ fn upperBound (self : * const LineIndex , target : usize ) usize {
594+ var left : usize = 0 ;
595+ var right : usize = self .line_starts .items .len ;
596+ while (left < right ) {
597+ const mid = left + (right - left ) / 2 ;
598+ if (self .line_starts .items [mid ] <= target ) {
599+ left = mid + 1 ;
600+ } else {
601+ right = mid ;
602+ }
603+ }
604+ return left ;
605+ }
606+
592607 pub fn rebuild (self : * LineIndex , buffer : * const Buffer ) ! void {
593608 errdefer self .valid = false ;
594609
@@ -679,13 +694,13 @@ pub const LineIndex = struct {
679694 /// 挿入時のインクリメンタル更新(O(改行数 + 影響行数))
680695 /// 再スキャンなしで行インデックスを更新する
681696 pub fn updateForInsert (self : * LineIndex , pos : usize , text : []const u8 ) ! void {
682- if (! self .valid ) return ; // 無効なら何もしない
697+ if (! self .valid or text . len == 0 ) return ; // 無効または空挿入なら何もしない
683698
684- // pos以降の全エントリにテキスト長を加算(行頭への挿入を含む )
685- for ( self .line_starts . items ) | * line_start | {
686- if ( line_start .* >= pos ) {
687- line_start .* += text . len ;
688- }
699+ // posより後のエントリのみシフト(行頭のline_start == posは動かさない )
700+ const shift_start = self .upperBound ( pos );
701+ var idx = shift_start ;
702+ while ( idx < self . line_starts . items . len ) : ( idx += 1 ) {
703+ self . line_starts . items [ idx ] += text . len ;
689704 }
690705
691706 // 挿入テキスト内の改行位置を検出して追加
@@ -700,17 +715,8 @@ pub const LineIndex = struct {
700715 }
701716
702717 if (new_lines .items .len > 0 ) {
703- // 挿入位置に対応するインデックスを見つける
704- var insert_idx : usize = self .line_starts .items .len ;
705- for (self .line_starts .items , 0.. ) | line_start , idx | {
706- if (line_start > pos ) {
707- insert_idx = idx ;
708- break ;
709- }
710- }
711-
712718 // 新しい行をその位置に挿入
713- try self .line_starts .insertSlice (self .allocator , insert_idx , new_lines .items );
719+ try self .line_starts .insertSlice (self .allocator , shift_start , new_lines .items );
714720 }
715721
716722 self .valid_until_pos += text .len ;
@@ -719,32 +725,30 @@ pub const LineIndex = struct {
719725 /// 削除時のインクリメンタル更新(O(削除行数 + 影響行数))
720726 /// 再スキャンなしで行インデックスを更新する
721727 pub fn updateForDelete (self : * LineIndex , pos : usize , count : usize , deleted_newlines : usize ) void {
722- if (! self .valid ) return ; // 無効なら何もしない
728+ if (! self .valid or count == 0 ) return ; // 無効なら何もしない
723729
724730 const end_pos = pos + count ;
725731
726732 // 削除範囲内の行エントリを削除
727733 if (deleted_newlines > 0 ) {
728- var write_idx : usize = 0 ;
729- for (self .line_starts .items ) | line_start | {
730- if (line_start < pos or line_start >= end_pos ) {
731- // 範囲外: 保持(end_pos以降は位置調整が必要)
732- if (line_start >= end_pos ) {
733- self .line_starts .items [write_idx ] = line_start - count ;
734- } else {
735- self .line_starts .items [write_idx ] = line_start ;
736- }
737- write_idx += 1 ;
734+ const remove_start = self .upperBound (pos );
735+ var write_idx = remove_start ;
736+ var read_idx = remove_start ;
737+ while (read_idx < self .line_starts .items .len ) : (read_idx += 1 ) {
738+ const line_start = self .line_starts .items [read_idx ];
739+ if (line_start <= end_pos ) {
740+ continue ; // 削除範囲内 (pos < line_start <= end_pos): エントリを落とす
738741 }
739- // 範囲内(pos <= line_start < end_pos)の行は削除(スキップ)
742+ self .line_starts .items [write_idx ] = line_start - count ;
743+ write_idx += 1 ;
740744 }
741745 self .line_starts .shrinkRetainingCapacity (write_idx );
742746 } else {
743747 // 改行削除なし: 位置の調整のみ
744- for ( self .line_starts . items ) | * line_start | {
745- if ( line_start .* >= end_pos ) {
746- line_start .* -= count ;
747- }
748+ const shift_start = self .upperBound ( end_pos );
749+ var idx = shift_start ;
750+ while ( idx < self . line_starts . items . len ) : ( idx += 1 ) {
751+ self . line_starts . items [ idx ] -= count ;
748752 }
749753 }
750754
@@ -2355,18 +2359,18 @@ pub const Buffer = struct {
23552359
23562360 var search_pos : usize = 0 ;
23572361 const limit = if (max_count == 0 ) std .math .maxInt (usize ) else max_count ;
2362+ var found_current = false ; // フラグで条件分岐を最小化
23582363
23592364 while (result .total < limit ) {
23602365 const match = self .searchForward (pattern , search_pos ) orelse break ;
23612366
23622367 result .total += 1 ;
23632368
23642369 // カーソル位置がマッチ範囲内または直後にある場合、このマッチが「現在」
2365- // Emacs風: 前方検索はマッチ終端にカーソルがあるので、match.start + match.len == cursor_pos
2366- if (result .current_index == null ) {
2367- if (cursor_pos >= match .start and cursor_pos <= match .start + match .len ) {
2368- result .current_index = result .total ; // 1-based
2369- }
2370+ // found_currentフラグで、見つかった後の不要なチェックをスキップ
2371+ if (! found_current and cursor_pos >= match .start and cursor_pos <= match .start + match .len ) {
2372+ result .current_index = result .total ; // 1-based
2373+ found_current = true ;
23702374 }
23712375
23722376 // 次の検索位置(空マッチ防止のため最低1バイト進める)
0 commit comments