/Support/lib/as3/parsers/property_inspector.rb

https://github.com/Stray/actionscript3-tmbundle · Ruby · 216 lines · 177 code · 6 blank · 33 comment · 1 complexity · 24010ff33148d128a92001e3f13b82e7 MD5 · raw file

  1. #!/usr/bin/env ruby -wKU
  2. # encoding: utf-8
  3. ################################################################################
  4. #
  5. # Copyright 2009-2010 Simon Gregory
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ################################################################################
  21. # Designed to scan from the caret location within the current document and
  22. # output a string that can be used by the class parser to load and store the
  23. # properteries, methods etc of the resulting class reference.
  24. #
  25. module PropertyInspector
  26. # Finds and returns a 'Property Chain' ie foo.bar.baz based on the
  27. # current line.
  28. #
  29. # Where it finds an object that has been type cast it's
  30. # class is inserted into the chain not it's property name. So
  31. # foo.( bar as Sprite ).baz becomes foo.Sprite.baz
  32. #
  33. def self.property_chain
  34. li = ENV['TM_LINE_INDEX']
  35. ln = ENV['TM_CURRENT_LINE']
  36. la = ln.split("")
  37. i = li.to_i-1
  38. # When we are on a completly blank line return "this"
  39. if ln =~ /^\s*$/
  40. return "this"
  41. end
  42. # Stop looking when we hit these chars.
  43. stop_match = /[\[\s=;,:"']/
  44. # Casting using the as operator.
  45. as_regexp = /\bas\b\s+(\w+)/
  46. # Holds the cars found
  47. found = []
  48. bracket_contents = []
  49. # int to track the level of nesting.
  50. nests = 0;
  51. while i >= 0
  52. current_letter = la[i]
  53. # Make sure we stop if we hit a closing nest char.
  54. break if current_letter == "("
  55. if current_letter =~ /\)/
  56. while i >= 0
  57. current_letter = la[i]
  58. i -= 1
  59. if current_letter =~ /\)/
  60. nests += 1
  61. elsif current_letter =~ /\(/
  62. nests -= 1
  63. end
  64. if nests == 0
  65. if bracket_contents.reverse.to_s =~ as_regexp
  66. found << $1
  67. end
  68. bracket_contents = []
  69. break
  70. end
  71. bracket_contents << current_letter
  72. end
  73. end
  74. break if current_letter =~ stop_match
  75. next if current_letter =~ /[()]/
  76. found << current_letter
  77. i -= 1
  78. end
  79. found.reverse!
  80. # Search for casting.
  81. if found.empty?
  82. if bracket_contents.reverse.to_s =~ as_regexp
  83. return $1
  84. end
  85. end
  86. return nil if found.empty?
  87. chain = found.to_s
  88. chain.chop! if chain =~ /\.$/
  89. return nil if chain == ""
  90. return chain
  91. end
  92. # Tests to see if the caret is currently at a class reference or a
  93. # property instance name.
  94. #
  95. # NOTE: Relies entirely on the convention that classes start with an
  96. # uppercase character.
  97. #
  98. def self.is_static
  99. li = ENV['TM_LINE_INDEX']
  100. ln = ENV['TM_CURRENT_LINE']
  101. la = ln.split("")
  102. i = li.to_i-1
  103. last_c = ""
  104. while i >= 0
  105. c = la[i]
  106. unless c =~ /[\w.]/
  107. if last_c =~ /[A-Z]/
  108. before = la[0..i].join()
  109. return false if before =~ /new\s+$/
  110. return true
  111. end
  112. return false
  113. end
  114. last_c = c
  115. i -= 1
  116. end
  117. return false
  118. end
  119. # Return a boolean depending on whether the caret is just after a '.'
  120. #
  121. def self.at_dot
  122. li = ENV['TM_LINE_INDEX']
  123. ln = ENV['TM_CURRENT_LINE']
  124. la = ln.split("")
  125. i = li.to_i-1
  126. return true if la[i] == "."
  127. return false
  128. end
  129. # Should autocompletion insert a dot before the completed statement.
  130. # Deprecated, in favour of scope in the language file.
  131. #
  132. def self.insert_dot
  133. li = ENV['TM_LINE_INDEX']
  134. ln = ENV['TM_CURRENT_LINE']
  135. la = ln.split("")
  136. i = li.to_i-1
  137. return false if la.length <= i
  138. return false if la[i] =~ /(\.|\s)/
  139. return true
  140. end
  141. # Single access point to collect all information about the current scope.
  142. #
  143. def self.capture
  144. state = {
  145. :ref => self.property_chain,
  146. :is_static => self.is_static,
  147. :insert_dot => self.insert_dot,
  148. :filter => nil
  149. }
  150. return state if state[:ref] == nil
  151. chain = state[:ref].split(".")
  152. # Do we need to filter output?
  153. unless ENV['TM_SCOPE'] =~ /following\.dot/
  154. if chain.size > 1
  155. state[:filter] = chain.pop()
  156. state[:ref] = chain.join(".")
  157. if state[:ref] == ''
  158. state[:ref] = "this"
  159. end
  160. elsif chain.size == 1 and chain[0] != "this" and state[:is_static] != true
  161. state[:ref] = "this"
  162. state[:filter] = chain[0]
  163. end
  164. end
  165. state
  166. end
  167. end