BigW Consortium Gitlab

snippet_search_results.rb 3.73 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
module Gitlab
  class SnippetSearchResults < SearchResults
    attr_reader :limit_snippet_ids

    def initialize(limit_snippet_ids, query)
      @limit_snippet_ids = limit_snippet_ids
      @query = query
    end

    def objects(scope, page = nil)
      case scope
      when 'snippet_titles'
        Kaminari.paginate_array(snippet_titles).page(page).per(per_page)
      when 'snippet_blobs'
        Kaminari.paginate_array(snippet_blobs).page(page).per(per_page)
      else
        super
      end
    end

    def total_count
      @total_count ||= snippet_titles_count + snippet_blobs_count
    end

    def snippet_titles_count
      @snippet_titles_count ||= snippet_titles.count
    end

    def snippet_blobs_count
      @snippet_blobs_count ||= snippet_blobs.count
    end

    private

    def snippet_titles
      Snippet.where(id: limit_snippet_ids).search(query).order('updated_at DESC')
    end

    def snippet_blobs
40 41
      search = Snippet.where(id: limit_snippet_ids).search_code(query)
      search = search.order('updated_at DESC').to_a
42
      snippets = []
43
      search.each { |e| snippets << chunk_snippet(e) }
44 45 46 47 48 49 50
      snippets
    end

    def default_scope
      'snippet_blobs'
    end

51 52 53 54 55
    # Get an array of line numbers surrounding a matching
    # line, bounded by min/max.
    #
    # @returns Array of line numbers
    def bounded_line_numbers(line, min, max)
56 57 58 59 60
      lower = line - surrounding_lines > min ? line - surrounding_lines : min
      upper = line + surrounding_lines < max ? line + surrounding_lines : max
      (lower..upper).to_a
    end

61 62 63 64 65 66
    # Returns a sorted set of lines to be included in a snippet preview.
    # This ensures matching adjacent lines do not display duplicated
    # surrounding code.
    #
    # @returns Array, unique and sorted.
    def matching_lines(lined_content)
67
      used_lines = []
68
      lined_content.each_with_index do |line, line_number|
69 70 71
        used_lines.concat bounded_line_numbers(
          line_number,
          0,
72
          lined_content.size
73
        ) if line.include?(query)
74
      end
75

76 77 78 79 80 81 82 83 84 85
      used_lines.uniq.sort
    end

    # 'Chunkify' entire snippet.  Splits the snippet data into matching lines +
    # surrounding_lines() worth of unmatching lines.
    #
    # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}}
    def chunk_snippet(snippet)
      lined_content = snippet.content.split("\n")
      used_lines = matching_lines(lined_content)
86 87 88 89 90

      snippet_chunk = []
      snippet_chunks = []
      snippet_start_line = 0
      last_line = -1
91 92 93

      # Go through each used line, and add consecutive lines as a single chunk
      # to the snippet chunk array.
94
      used_lines.each do |line_number|
95
        if last_line < 0
96
          # Start a new chunk.
97 98 99
          snippet_start_line = line_number
          snippet_chunk << lined_content[line_number]
        elsif last_line == line_number - 1
100
          # Consecutive line, continue chunk.
101 102
          snippet_chunk << lined_content[line_number]
        else
103
          # Non-consecutive line, add chunk to chunk array.
104 105 106 107
          snippet_chunks << {
            data: snippet_chunk.join("\n"),
            start_line: snippet_start_line + 1
          }
108 109

          # Start a new chunk.
110 111 112 113
          snippet_chunk = [lined_content[line_number]]
          snippet_start_line = line_number
        end
        last_line = line_number
114
      end
115
      # Add final chunk to chunk array
116 117 118 119 120
      snippet_chunks << {
        data: snippet_chunk.join("\n"),
        start_line: snippet_start_line + 1
      }

121
      # Return snippet with chunk array
122 123
      { snippet_object: snippet, snippet_chunks: snippet_chunks }
    end
124 125 126 127 128 129

    # Defines how many unmatching lines should be
    # included around the matching lines in a snippet
    def surrounding_lines
      3
    end
130 131
  end
end