瀏覽代碼

Maximise displayed text for hover popup

Previously we relied on popup_atcursor to display the popup in a
sensible place. However, by default 'wrap' is enabled, which prevents
vim from shifting the popup to the left to show it on the screen (even
if you set minwidth). As a result it was possible for the popup to
display a very small vertical section when triggered close to the
right-hand screen edge.

The solution is to enable wrapping, which lets vim shift the popup. The
edge-case is that we _do_ want wrapping if there are lines that are
likely to be wrapped. Therefore in that case (when any line is > some
maximum width), we just force the popup to be placed at column 1 and
enable wrapping.

The maximum width for this wrapping is calculated as the screen width in
columns minus the padding we add in the x-axis. THis is a bit hacky as
it doesn't work properly for multi-byte characters, though the
calculation is likely to be _conservative_ in taht case, so it's
probably ok.
Ben Jackson 4 年之前
父節點
當前提交
c87391ea5b
共有 3 個文件被更改,包括 103 次插入5 次删除
  1. 25 2
      autoload/youcompleteme.vim
  2. 54 3
      test/hover.test.vim
  3. 24 0
      test/testdata/python/doc.py

+ 25 - 2
autoload/youcompleteme.vim

@@ -1338,13 +1338,36 @@ if exists( '*popup_atcursor' )
     endif
 
     call popup_hide( s:cursorhold_popup )
+
+    " Try to position the popup at the cursor, but avoid wrapping. If the
+    " longest line is > screen width (&columns), then we just have to wrap, and
+    " place the popup at the leftmost column.
+    "
+    " Find the longest line (FIXME: probably doesn't work well for multi-byte)
+    let lines = split( response, "\n" )
+    let len = max( map( copy( lines ), "len( v:val )" ) )
+
+    let wrap = 0
+    let col = 'cursor'
+
+    " max width is screen columns minus x padding (2)
+    if len >= (&columns - 2)
+      " There's at least one line > our max - enable word wrap and draw the
+      " popup at the leftmost column
+      let col = 1
+      let wrap = 1
+    endif
+
     let s:cursorhold_popup = popup_atcursor(
-          \   split( response, "\n" ),
+          \   lines,
           \   {
+          \     'col': col,
+          \     'wrap': wrap,
           \     'padding': [ 0, 1, 0, 1 ],
-          \     'maxwidth': &columns,
           \     'moved': 'word',
+          \     'maxwidth': &columns,
           \     'close': 'click',
+          \     'fixed': 0,
           \   }
           \ )
     call setbufvar( winbufnr( s:cursorhold_popup ),

+ 54 - 3
test/hover.test.vim

@@ -14,13 +14,15 @@ function! s:CheckPopupVisibleScreenPos( loc, text, syntax )
   let popup = popup_locate( a:loc.row, a:loc.col )
   call assert_notequal( 0,
                       \ popup,
-                      \ 'Locate popup at '
+                      \ 'Locate popup at ('
                       \ . a:loc.row
                       \ . ','
                       \ . a:loc.col
                       \ . ')' )
-  call assert_equal( a:text,
-                   \ getbufline( winbufnr( popup ), 1, '$' ) )
+  if a:text isnot v:none
+    call assert_equal( a:text,
+                     \ getbufline( winbufnr( popup ), 1, '$' ) )
+  endif
   call assert_equal( a:syntax, getbufvar( winbufnr( popup ), '&syntax' ) )
 endfunction
 
@@ -373,3 +375,52 @@ endfunction
 function! TearDown_Test_Hover_Custom_Command()
   au! MyYCMCustom
 endfunction
+
+function! Test_Long_Single_Line()
+  call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
+  call cursor( [ 37, 3 ] )
+  normal \D
+
+  " The popup should cover at least the whole of the line above, and not the
+  " current line
+  call s:CheckPopupVisible( 36, 1, v:none, '' )
+  call s:CheckPopupVisible( 36, &columns, v:none, '' )
+
+  call s:CheckPopupNotVisible( 37, 1 )
+  call s:CheckPopupNotVisible( 37, &columns )
+
+  " Also wrap is ON so it should cover at least 2 lines + 2 for the header/empty
+  " line
+  call s:CheckPopupVisible( 35, 1, v:none, '' )
+  call s:CheckPopupVisible( 35, &columns, v:none, '' )
+  call s:CheckPopupVisible( 33, 1, v:none, '' )
+  call s:CheckPopupVisible( 33, &columns, v:none, '' )
+
+  call popup_clear()
+  %bwipe!
+endfunction
+
+function! Test_Long_Wrapped()
+  call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
+  call cursor( [ 38, 22 ] )
+  normal \D
+
+  " The popup should cover at least the whole of the line above, and not the
+  " current line. In this case, it's because the popup was shifted.
+  call s:CheckPopupVisible( 37, 1, v:none, '' )
+  call s:CheckPopupVisible( 37, &columns, v:none, '' )
+
+  call s:CheckPopupNotVisible( 38, 1 )
+  call s:CheckPopupNotVisible( 38, &columns )
+
+  " Also, wrap is off, so it should be _exactly_ 9 lines + 2 for the signature
+  " and the empty line
+  call s:CheckPopupVisible( 27, 1, v:none, '' )
+  call s:CheckPopupVisible( 27, &columns, v:none, '' )
+
+  call s:CheckPopupNotVisible( 26, 1 )
+  call s:CheckPopupNotVisible( 26, &columns )
+
+  call popup_clear()
+  %bwipe!
+endfunction

+ 24 - 0
test/testdata/python/doc.py

@@ -12,3 +12,27 @@ def Main():
   Test_OneLine()
   Test_MultiLine()
 
+
+def Really_Long_Method( which, has, some, param, that, take, the, whole, line ):
+  """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum egestas libero urna, vel sagittis felis condimentum in. Nulla arcu eros, aliquet vel mollis vitae, semper eu ex. Donec posuere quam et ornare sagittis. Curabitur nunc ex, fringilla quis lorem sed, dignissim congue felis. Integer vestibulum ac elit vel blandit. Nam non dui urna. Integer eu semper massa. Nullam ac elit interdum, aliquet elit nec, porttitor orci. Duis tempus justo lorem, ac fringilla ante viverra egestas. Etiam eleifend enim ac libero dapibus, quis condimentum lectus tristique. Fusce feugiat, lorem et faucibus eleifend, ipsum nisi maximus justo, at consectetur ligula leo vitae justo."""
+  # Really long one-line
+  pass
+
+
+def Really_Long_Method_2():
+  """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum egestas
+  libero urna, vel sagittis felis condimentum in. Nulla arcu eros, aliquet vel
+  mollis vitae, semper eu ex. Donec posuere quam et ornare sagittis. Curabitur
+  nunc ex, fringilla quis lorem sed, dignissim congue felis. Integer vestibulum
+  ac elit vel blandit. Nam non dui urna. Integer eu semper massa. Nullam ac elit
+  interdum, aliquet elit nec, porttitor orci. Duis tempus justo lorem, ac
+  fringilla ante viverra egestas. Etiam eleifend enim ac libero dapibus, quis
+  condimentum lectus tristique. Fusce feugiat, lorem et faucibus eleifend, ipsum
+  nisi maximus justo, at consectetur ligula leo vitae justo."""
+  # Really long one para
+  pass
+
+
+def Moan():
+  Really_Long_Method()
+  Really_Long_Method_2()