@@ -50,13 +50,68 @@ define(function (require, exports, module) {
5050 // is shown in a separate popup beside the hint list so the list itself never reflows while
5151 // navigating with the arrow keys.
5252 var $lspDocPopup = null ;
53+ // The hint list reflows after _showDocPopup runs - it flips above/below the caret near a screen
54+ // edge, shifts as the cursor moves, and moves on scroll. To keep the doc popup glued beside it
55+ // (and never overlapping it), we re-derive its position from the list's *current* rect on every
56+ // animation frame while it is visible, instead of positioning it once.
57+ var _docTrackRAF = null , // active requestAnimationFrame handle, or null when not tracking
58+ _docTrackList = null , // the ul.dropdown-menu the popup is anchored to
59+ _docLastPos = "" ; // last applied "left,top" - skip the css write when unchanged
5360
5461 function _hideDocPopup ( ) {
62+ if ( _docTrackRAF ) {
63+ cancelAnimationFrame ( _docTrackRAF ) ;
64+ _docTrackRAF = null ;
65+ }
66+ _docTrackList = null ;
67+ _docLastPos = "" ;
5568 if ( $lspDocPopup ) {
5669 $lspDocPopup . hide ( ) . empty ( ) ;
5770 }
5871 }
5972
73+ /**
74+ * Place the doc popup flush beside the hint list's current position: to the right, flipping to
75+ * the left when there isn't room, and clamped vertically to stay on screen. Reads the list's
76+ * live rect so it stays correct no matter how the list has since moved.
77+ */
78+ function _positionDocPopup ( ) {
79+ if ( ! $lspDocPopup || ! _docTrackList || ! _docTrackList . length ) {
80+ return ;
81+ }
82+ var listEl = _docTrackList [ 0 ] ;
83+ if ( ! listEl . isConnected ) {
84+ // The hint menu (and this popup, its child) was torn down - stop tracking.
85+ _hideDocPopup ( ) ;
86+ return ;
87+ }
88+ var anchor = listEl . getBoundingClientRect ( ) ;
89+ if ( anchor . width === 0 && anchor . height === 0 ) {
90+ return ; // list not laid out yet (mid-reflow) - try again next frame
91+ }
92+ var GAP = 6 ,
93+ winW = $ ( window ) . width ( ) ,
94+ winH = $ ( window ) . height ( ) ,
95+ pw = $lspDocPopup . outerWidth ( ) ,
96+ ph = $lspDocPopup . outerHeight ( ) ,
97+ left = anchor . right + GAP ;
98+ if ( left + pw > winW - 8 ) {
99+ left = anchor . left - pw - GAP ; // not enough room on the right - flip to the left
100+ }
101+ left = Math . max ( 8 , left ) ;
102+ var top = Math . max ( 8 , Math . min ( anchor . top , winH - ph - 8 ) ) ;
103+ var pos = Math . round ( left ) + "," + Math . round ( top ) ;
104+ if ( pos !== _docLastPos ) {
105+ _docLastPos = pos ;
106+ $lspDocPopup . css ( { left : left , top : top } ) ;
107+ }
108+ }
109+
110+ function _trackDocPopup ( ) {
111+ _positionDocPopup ( ) ;
112+ _docTrackRAF = requestAnimationFrame ( _trackDocPopup ) ;
113+ }
114+
60115 // Syntax-highlight fenced code blocks (the signature/examples in completion docs) with the
61116 // globally available highlight.js, so they read like code instead of flat monospace. Theme-aware
62117 // token colours live in src/styles/brackets.less (.lsp-hint-doc-popup, shared with the hover).
@@ -117,21 +172,16 @@ define(function (require, exports, module) {
117172 if ( ! $list . length ) {
118173 $list = $menu ;
119174 }
120- var anchor = $list [ 0 ] . getBoundingClientRect ( ) ;
121175
122- var GAP = 6 ;
123- // Measure, then place to the right of the hint list - flipping to the left when there
124- // isn't enough room.
125- $lspDocPopup . css ( { display : "block" , visibility : "hidden" , left : 0 , top : 0 } ) ;
126- var winW = $ ( window ) . width ( ) , winH = $ ( window ) . height ( ) ,
127- pw = $lspDocPopup . outerWidth ( ) , ph = $lspDocPopup . outerHeight ( ) ,
128- left = anchor . right + GAP ;
129- if ( left + pw > winW - 8 ) {
130- left = anchor . left - pw - GAP ; // not enough room on the right - flip to the left
176+ // Position now, then keep re-positioning every frame so the popup follows the list wherever
177+ // it reflows to (and never ends up overlapping it).
178+ _docTrackList = $list ;
179+ _docLastPos = "" ;
180+ $lspDocPopup . css ( { display : "block" } ) ;
181+ _positionDocPopup ( ) ;
182+ if ( ! _docTrackRAF ) {
183+ _trackDocPopup ( ) ;
131184 }
132- left = Math . max ( 8 , left ) ;
133- var top = Math . min ( anchor . top , Math . max ( 8 , winH - ph - 8 ) ) ;
134- $lspDocPopup . css ( { left : left , top : top , visibility : "visible" } ) ;
135185 }
136186
137187 function _injectInlineSignature ( $labelSpan , detail ) {
0 commit comments