File: stack.rb

package info (click to toggle)
ruby-sass 3.7.4-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,396 kB
  • sloc: ruby: 32,443; sh: 26; makefile: 25
file content (140 lines) | stat: -rw-r--r-- 4,153 bytes parent folder | download | duplicates (4)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
module Sass
  # A class representing the stack when compiling a Sass file.
  class Stack
    # TODO: use this to generate stack information for Sass::SyntaxErrors.

    # A single stack frame.
    class Frame
      # The filename of the file in which this stack frame was created.
      #
      # @return [String]
      attr_reader :filename

      # The line number on which this stack frame was created.
      #
      # @return [String]
      attr_reader :line

      # The type of this stack frame. This can be `:import`, `:mixin`, or
      # `:base`.
      #
      # `:base` indicates that this is the bottom-most frame, meaning that it
      # represents a single line of code rather than a nested context. The stack
      # will only ever have one base frame, and it will always be the most
      # deeply-nested frame.
      #
      # @return [Symbol?]
      attr_reader :type

      # The name of the stack frame. For mixin frames, this is the mixin name;
      # otherwise, it's `nil`.
      #
      # @return [String?]
      attr_reader :name

      def initialize(filename, line, type, name = nil)
        @filename = filename
        @line = line
        @type = type
        @name = name
      end

      # Whether this frame represents an import.
      #
      # @return [Boolean]
      def is_import?
        type == :import
      end

      # Whether this frame represents a mixin.
      #
      # @return [Boolean]
      def is_mixin?
        type == :mixin
      end

      # Whether this is the base frame.
      #
      # @return [Boolean]
      def is_base?
        type == :base
      end
    end

    # The stack frames. The last frame is the most deeply-nested.
    #
    # @return [Array<Frame>]
    attr_reader :frames

    def initialize
      @frames = []
    end

    # Pushes a base frame onto the stack.
    #
    # @param filename [String] See \{Frame#filename}.
    # @param line [String] See \{Frame#line}.
    # @yield [] A block in which the new frame is on the stack.
    def with_base(filename, line)
      with_frame(filename, line, :base) {yield}
    end

    # Pushes an import frame onto the stack.
    #
    # @param filename [String] See \{Frame#filename}.
    # @param line [String] See \{Frame#line}.
    # @yield [] A block in which the new frame is on the stack.
    def with_import(filename, line)
      with_frame(filename, line, :import) {yield}
    end

    # Pushes a mixin frame onto the stack.
    #
    # @param filename [String] See \{Frame#filename}.
    # @param line [String] See \{Frame#line}.
    # @param name [String] See \{Frame#name}.
    # @yield [] A block in which the new frame is on the stack.
    def with_mixin(filename, line, name)
      with_frame(filename, line, :mixin, name) {yield}
    end

    # Pushes a function frame onto the stack.
    #
    # @param filename [String] See \{Frame#filename}.
    # @param line [String] See \{Frame#line}.
    # @param name [String] See \{Frame#name}.
    # @yield [] A block in which the new frame is on the stack.
    def with_function(filename, line, name)
      with_frame(filename, line, :function, name) {yield}
    end

    # Pushes a function frame onto the stack.
    #
    # @param filename [String] See \{Frame#filename}.
    # @param line [String] See \{Frame#line}.
    # @param name [String] See \{Frame#name}.
    # @yield [] A block in which the new frame is on the stack.
    def with_directive(filename, line, name)
      with_frame(filename, line, :directive, name) {yield}
    end

    def to_s
      (frames.reverse + [nil]).each_cons(2).each_with_index.
          map do |(frame, caller), i|
        "#{i == 0 ? 'on' : 'from'} line #{frame.line}" +
          " of #{frame.filename || 'an unknown file'}" +
          (caller && caller.name ? ", in `#{caller.name}'" : "")
      end.join("\n")
    end

    private

    def with_frame(filename, line, type, name = nil)
      @frames.pop if @frames.last && @frames.last.type == :base
      @frames.push(Frame.new(filename, line, type, name))
      yield
    ensure
      @frames.pop unless type == :base && @frames.last && @frames.last.type != :base
    end
  end
end