@@ -723,6 +723,74 @@ function Render:row_wrapped_lines(row, row_index)
723723 return result
724724end
725725
726+ --- @private
727+ --- @param text string
728+ --- @param win_width integer
729+ --- @return integer[]
730+ function Render :wrapped_slots (text , win_width )
731+ if # text == 0 then
732+ return { 0 }
733+ end
734+
735+ local linebreak = env .win .get (self .context .win , ' linebreak' ) == true
736+ local breakat = vim .api .nvim_get_option_value (' breakat' , {})
737+ local showbreak = env .win .get (self .context .win , ' showbreak' )
738+ local breakindent = env .win .get (self .context .win , ' breakindent' ) == true
739+ local indent = breakindent and str .spaces (' start' , text ) or 0
740+ local continuation_width =
741+ math.max (win_width - str .width (tostring (showbreak )) - indent , 1 )
742+
743+ local chars = {} --- @type { col : integer , text : string , width : integer } []
744+ local bytes = vim .str_utf_pos (text )
745+ for index , start_byte in ipairs (bytes ) do
746+ local end_byte = index < # bytes and bytes [index + 1 ] - 1 or # text
747+ local char = text :sub (start_byte , end_byte )
748+ chars [# chars + 1 ] = {
749+ col = start_byte - 1 ,
750+ text = char ,
751+ width = str .width (char ),
752+ }
753+ end
754+
755+ local slots = {} --- @type integer[]
756+ local index = 1
757+ local first = true
758+ while index <= # chars do
759+ slots [# slots + 1 ] = chars [index ].col
760+ local width = first and win_width or continuation_width
761+ local used = 0
762+ local next_index = index
763+ local break_index = nil --- @type integer ?
764+ while next_index <= # chars do
765+ local char = chars [next_index ]
766+ if used > 0 and used + char .width > width then
767+ break
768+ end
769+ used = used + char .width
770+ if linebreak and breakat :find (char .text , 1 , true ) then
771+ break_index = next_index
772+ end
773+ next_index = next_index + 1
774+ if used >= width then
775+ break
776+ end
777+ end
778+ if next_index > # chars then
779+ break
780+ elseif linebreak and break_index and break_index >= index then
781+ index = break_index + 1
782+ while index <= # chars and chars [index ].text :match (' ^%s$' ) do
783+ index = index + 1
784+ end
785+ else
786+ index = next_index
787+ end
788+ first = false
789+ end
790+
791+ return slots
792+ end
793+
726794--- @private
727795function Render :wrapped ()
728796 local visual = {} --- @type render.md.Line[]
@@ -774,15 +842,8 @@ function Render:wrapped()
774842 conceal = ' ' ,
775843 })
776844 end
777- local screen_lines =
778- math.max (math.ceil (str .width (buf_line ) / win_width ), 1 )
779- for line = 0 , screen_lines - 1 do
780- local byte_col = line == 0 and 0
781- or vim .fn .byteidx (buf_line , line * win_width )
782- if byte_col < 0 then
783- byte_col = # buf_line
784- end
785- slots [# slots + 1 ] = { row = node .start_row , col = byte_col }
845+ for _ , col in ipairs (self :wrapped_slots (buf_line , win_width )) do
846+ slots [# slots + 1 ] = { row = node .start_row , col = col }
786847 end
787848 end
788849
@@ -793,6 +854,7 @@ function Render:wrapped()
793854 self .marks :add (self .config , ' table_border' , slot .row , slot .col , {
794855 virt_text = line :get (),
795856 virt_text_pos = ' overlay' ,
857+ virt_text_win_col = 0 ,
796858 hl_mode = ' combine' ,
797859 })
798860 else
0 commit comments