summaryrefslogtreecommitdiff
path: root/lib/syntax_suggest/capture/before_after_keyword_ends.rb
blob: f53c57a4d161d4cea00936bb5eabd9a788eb6292 (plain)
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# frozen_string_literal: true

module SyntaxSuggest
  module Capture
    # Shows surrounding kw/end pairs
    #
    # The purpose of showing these extra pairs is due to cases
    # of ambiguity when only one visible line is matched.
    #
    # For example:
    #
    #     1  class Dog
    #     2    def bark
    #     4    def eat
    #     5    end
    #     6  end
    #
    # In this case either line 2 could be missing an `end` or
    # line 4 was an extra line added by mistake (it happens).
    #
    # When we detect the above problem it shows the issue
    # as only being on line 2
    #
    #     2    def bark
    #
    # Showing "neighbor" keyword pairs gives extra context:
    #
    #     2    def bark
    #     4    def eat
    #     5    end
    #
    #
    # Example:
    #
    #   lines = BeforeAfterKeywordEnds.new(
    #     block: block,
    #     code_lines: code_lines
    #   ).call()
    #
    class BeforeAfterKeywordEnds
      def initialize(code_lines:, block:)
        @scanner = ScanHistory.new(code_lines: code_lines, block: block)
        @original_indent = block.current_indent
      end

      def call
        lines = []

        @scanner.scan(
          up: ->(line, kw_count, end_count) {
            next true if line.empty?
            break if line.indent < @original_indent
            next true if line.indent != @original_indent

            # If we're going up and have one complete kw/end pair, stop
            if kw_count != 0 && kw_count == end_count
              lines << line
              break
            end

            lines << line if line.is_kw? || line.is_end?
            true
          },
          down: ->(line, kw_count, end_count) {
            next true if line.empty?
            break if line.indent < @original_indent
            next true if line.indent != @original_indent

            # if we're going down and have one complete kw/end pair,stop
            if kw_count != 0 && kw_count == end_count
              lines << line
              break
            end

            lines << line if line.is_kw? || line.is_end?
            true
          }
        )
        @scanner.stash_changes

        lines
      end
    end
  end
end