Sha256: 68ff927bad807bca45ebf27f1870a2cb8f450056dce4c8a3ffbaf57350676716

Contents?: true

Size: 1.71 KB

Versions: 1

Compression:

Stored size: 1.71 KB

Contents

# frozen_string_literal: true

module Parser

  class Lexer::Dedenter
    # Tab (\t) counts as 8 spaces
    TAB_WIDTH = 8

    def initialize(dedent_level)
      @dedent_level = dedent_level
      @at_line_begin = true
      @indent_level  = 0
    end

    # For a heredoc like
    #   <<-HERE
    #     a
    #     b
    #   HERE
    # this method gets called with "  a\n" and "  b\n"
    #
    # However, the following heredoc:
    #
    #   <<-HERE
    #     a\
    #     b
    #   HERE
    # calls this method only once with a string "  a\\\n  b\n"
    #
    # This is important because technically it's a single line,
    # but it has to be concatenated __after__ dedenting.
    #
    # It has no effect for non-squiggly heredocs, i.e. it simply removes "\\\n"
    # Of course, lexer could do it but once again: it's all because of dedenting.
    #
    def dedent(string)
      lines = string.split("\\\n")

      if @at_line_begin
        lines_to_dedent = lines
      else
        _first, *lines_to_dedent = lines
      end

      lines_to_dedent.each do |line|
        left_to_remove = @dedent_level
        remove = 0

        line.each_char do |char|
          break if left_to_remove <= 0
          case char
          when ?\s
            remove += 1
            left_to_remove -= 1
          when ?\t
            break if TAB_WIDTH * (remove / TAB_WIDTH + 1) > @dedent_level
            remove += 1
            left_to_remove -= TAB_WIDTH
          else
            # no more spaces or tabs
            break
          end
        end

        line.slice!(0, remove)
      end

      string.replace(lines.join)

      @at_line_begin = string.end_with?("\n")
    end

    def interrupt
      @at_line_begin = false
    end
  end

end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
parser-2.7.0.0 lib/parser/lexer/dedenter.rb