watchman_file_scanner.rb 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. # Copyright 2014 Wincent Colaiuta. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions are met:
  5. #
  6. # 1. Redistributions of source code must retain the above copyright notice,
  7. # this list of conditions and the following disclaimer.
  8. # 2. Redistributions in binary form must reproduce the above copyright notice,
  9. # this list of conditions and the following disclaimer in the documentation
  10. # and/or other materials provided with the distribution.
  11. #
  12. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  13. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  14. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  15. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  16. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  17. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  18. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  19. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  20. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  21. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  22. # POSSIBILITY OF SUCH DAMAGE.
  23. require 'pathname'
  24. require 'socket'
  25. require 'command-t/vim'
  26. require 'command-t/vim/path_utilities'
  27. require 'command-t/scanner/file_scanner'
  28. require 'command-t/scanner/file_scanner/find_file_scanner'
  29. module CommandT
  30. class FileScanner
  31. # A FileScanner which delegates the heavy lifting to Watchman
  32. # (https://github.com/facebook/watchman); useful for very large hierarchies.
  33. #
  34. # Inherits from FindFileScanner so that it can fall back to it in the event
  35. # that Watchman isn't available or able to fulfil the request.
  36. class WatchmanFileScanner < FindFileScanner
  37. # Exception raised when Watchman is unavailable or unable to process the
  38. # requested path.
  39. class WatchmanUnavailable < RuntimeError; end
  40. def paths
  41. @paths[@path] ||= begin
  42. ensure_cache_under_limit
  43. sockname = Watchman::Utils.load(
  44. %x{watchman --output-encoding=bser get-sockname}
  45. )['sockname']
  46. raise WatchmanUnavailable unless $?.exitstatus.zero?
  47. UNIXSocket.open(sockname) do |socket|
  48. root = Pathname.new(@path).realpath.to_s
  49. roots = Watchman::Utils.query(['watch-list'], socket)['roots']
  50. if !roots.include?(root)
  51. # this path isn't being watched yet; try to set up watch
  52. result = Watchman::Utils.query(['watch', root], socket)
  53. # root_restrict_files setting may prevent Watchman from working
  54. raise WatchmanUnavailable if result.has_key?('error')
  55. end
  56. query = ['query', root, {
  57. 'expression' => ['type', 'f'],
  58. 'fields' => ['name'],
  59. }]
  60. paths = Watchman::Utils.query(query, socket)
  61. # could return error if watch is removed
  62. raise WatchmanUnavailable if paths.has_key?('error')
  63. @paths[@path] = paths['files']
  64. end
  65. end
  66. @paths[@path]
  67. rescue Errno::ENOENT, WatchmanUnavailable
  68. # watchman executable not present, or unable to fulfil request
  69. super
  70. end
  71. end # class WatchmanFileScanner
  72. end # class FileScanner
  73. end # module CommandT