scrolling_range.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # Copyright (C) 2023, YouCompleteMe Contributors
  2. #
  3. # This file is part of YouCompleteMe.
  4. #
  5. # YouCompleteMe is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # YouCompleteMe is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  17. import abc
  18. from ycm import vimsupport
  19. class ScrollingBufferRange( object ):
  20. """Abstraction used by inlay hints and semantic tokens to only request visible
  21. ranges"""
  22. # FIXME: Send a request per-disjoint range for this buffer rather than the
  23. # maximal range. then collaate the results when all responses are returned
  24. def __init__( self, bufnr ):
  25. self._bufnr = bufnr
  26. self._tick = -1
  27. self._request = None
  28. self._last_requested_range = None
  29. def Ready( self ):
  30. return self._request is not None and self._request.Done()
  31. def Request( self, force=False ):
  32. if self._request and not self.Ready():
  33. return True
  34. # Check to see if the buffer ranges would actually change anything visible.
  35. # This avoids a round-trip for every single line scroll event
  36. if ( not force and
  37. self._tick == vimsupport.GetBufferChangedTick( self._bufnr ) and
  38. vimsupport.VisibleRangeOfBufferOverlaps(
  39. self._bufnr,
  40. self._last_requested_range ) ):
  41. return False # don't poll
  42. # FIXME: This call is duplicated in the call to VisibleRangeOfBufferOverlaps
  43. # - remove the expansion param
  44. # - look up the actual visible range, then call this function
  45. # - if not overlapping, do the factor expansion and request
  46. self._last_requested_range = vimsupport.RangeVisibleInBuffer( self._bufnr )
  47. # If this is false, either the self._bufnr is not a valid buffer number or
  48. # the buffer is not visible in any window.
  49. # Since this is called asynchronously, a user may bwipeout a buffer with
  50. # self._bufnr number between polls.
  51. if self._last_requested_range is None:
  52. return False
  53. self._tick = vimsupport.GetBufferChangedTick( self._bufnr )
  54. # We'll never use the last response again, so clear it
  55. self._latest_response = None
  56. self._request = self._NewRequest( self._last_requested_range )
  57. self._request.Start()
  58. return True
  59. def Update( self ):
  60. if not self._request:
  61. # Nothing to update
  62. return True
  63. assert self.Ready()
  64. # We're ready to use this response. Clear the request (to avoid repeatedly
  65. # re-polling).
  66. self._latest_response = self._request.Response()
  67. self._request = None
  68. if self._tick != vimsupport.GetBufferChangedTick( self._bufnr ):
  69. # Buffer has changed, we should ignore the data and retry
  70. self.Request( force=True )
  71. return False # poll again
  72. self._Draw()
  73. # No need to re-poll
  74. return True
  75. def Refresh( self ):
  76. if self._tick != vimsupport.GetBufferChangedTick( self._bufnr ):
  77. # stale data
  78. return
  79. if self._request is not None:
  80. # request in progress; we''l handle refreshing when it's done.
  81. return
  82. self._Draw()
  83. def GrowRangeIfNeeded( self, rng ):
  84. """When processing results, we may receive a wider range than requested. In
  85. that case, grow our 'last requested' range to minimise requesting more
  86. frequently than we need to."""
  87. # Note: references (pointers) so no need to re-assign
  88. rmin = self._last_requested_range[ 'start' ]
  89. rmax = self._last_requested_range[ 'end' ]
  90. start = rng[ 'start' ]
  91. end = rng[ 'end' ]
  92. if rmin[ 'line_num' ] is None or start[ 'line_num' ] < rmin[ 'line_num' ]:
  93. rmin[ 'line_num' ] = start[ 'line_num' ]
  94. rmin[ 'column_num' ] = start[ 'column_num' ]
  95. elif start[ 'line_num' ] == rmin[ 'line_num' ]:
  96. rmin[ 'column_num' ] = min( start[ 'column_num' ],
  97. rmin[ 'column_num' ] )
  98. if rmax[ 'line_num' ] is None or end[ 'line_num' ] > rmax[ 'line_num' ]:
  99. rmax[ 'line_num' ] = end[ 'line_num' ]
  100. rmax[ 'column_num' ] = end[ 'column_num' ]
  101. elif end[ 'line_num' ] == rmax[ 'line_num' ]:
  102. rmax[ 'column_num' ] = max( end[ 'column_num' ], rmax[ 'column_num' ] )
  103. # API; just implement the following, using self._bufnr and
  104. # self._latest_response as required
  105. @abc.abstractmethod
  106. def _NewRequest( self, request_range ):
  107. # prepare a new request_data and return it
  108. pass
  109. @abc.abstractmethod
  110. def _Draw( self ):
  111. # actuall paint the properties
  112. pass