rtext 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/CHANGELOG +20 -0
  2. data/{README → README.rdoc} +5 -1
  3. data/RText_Protocol +444 -0
  4. data/Rakefile +10 -10
  5. data/lib/rtext/completer.rb +32 -26
  6. data/lib/rtext/context_builder.rb +113 -59
  7. data/lib/rtext/default_loader.rb +73 -8
  8. data/lib/rtext/default_service_provider.rb +30 -14
  9. data/lib/rtext/frontend/config.rb +58 -0
  10. data/lib/rtext/frontend/connector.rb +233 -0
  11. data/lib/rtext/frontend/connector_manager.rb +81 -0
  12. data/lib/rtext/frontend/context.rb +56 -0
  13. data/lib/rtext/generic.rb +13 -0
  14. data/lib/rtext/instantiator.rb +30 -7
  15. data/lib/rtext/language.rb +54 -27
  16. data/lib/rtext/link_detector.rb +57 -0
  17. data/lib/rtext/message_helper.rb +77 -0
  18. data/lib/rtext/parser.rb +19 -68
  19. data/lib/rtext/serializer.rb +18 -3
  20. data/lib/rtext/service.rb +182 -118
  21. data/lib/rtext/tokenizer.rb +102 -0
  22. data/test/completer_test.rb +327 -70
  23. data/test/context_builder_test.rb +671 -91
  24. data/test/instantiator_test.rb +153 -0
  25. data/test/integration/backend.out +10 -0
  26. data/test/integration/crash_on_request_editor.rb +12 -0
  27. data/test/integration/ecore_editor.rb +50 -0
  28. data/test/integration/frontend.log +25138 -0
  29. data/test/integration/model/invalid_encoding.invenc +2 -0
  30. data/test/integration/model/test.crash_on_request +18 -0
  31. data/test/integration/model/test.crashing_backend +18 -0
  32. data/test/integration/model/test.dont_open_socket +0 -0
  33. data/test/integration/model/test.invalid_cmd_line +0 -0
  34. data/test/integration/model/test.not_in_rtext +0 -0
  35. data/test/integration/model/test_large_with_errors.ect3 +43523 -0
  36. data/test/integration/model/test_metamodel.ect +18 -0
  37. data/test/integration/model/test_metamodel2.ect +5 -0
  38. data/test/integration/model/test_metamodel_error.ect2 +3 -0
  39. data/test/integration/model/test_metamodel_ok.ect2 +18 -0
  40. data/test/integration/test.rb +684 -0
  41. data/test/link_detector_test.rb +276 -0
  42. data/test/message_helper_test.rb +118 -0
  43. data/test/rtext_test.rb +4 -1
  44. data/test/serializer_test.rb +96 -1
  45. data/test/tokenizer_test.rb +125 -0
  46. metadata +36 -10
  47. data/RText_Plugin_Implementation_Guide +0 -268
  48. data/lib/rtext_plugin/connection_manager.rb +0 -59
data/Rakefile CHANGED
@@ -1,39 +1,39 @@
1
- require 'rake/gempackagetask'
2
- require 'rake/rdoctask'
1
+ require 'rubygems/package_task'
2
+ require 'rdoc/task'
3
3
 
4
4
  DocFiles = [
5
- "README", "CHANGELOG", "MIT-LICENSE",
5
+ "README.rdoc", "CHANGELOG", "MIT-LICENSE",
6
6
  "RText_Users_Guide",
7
- "RText_Plugin_Implementation_Guide"]
7
+ "RText_Protocol"]
8
8
 
9
9
  RTextGemSpec = Gem::Specification.new do |s|
10
10
  s.name = %q{rtext}
11
- s.version = "0.4.0"
11
+ s.version = "0.5.0"
12
12
  s.date = Time.now.strftime("%Y-%m-%d")
13
13
  s.summary = %q{Ruby Textual Modelling}
14
14
  s.email = %q{martin dot thiede at gmx de}
15
15
  s.homepage = %q{http://ruby-gen.org}
16
16
  s.description = %q{RText can be used to derive textual languages from an RGen metamodel with very little effort.}
17
17
  s.authors = ["Martin Thiede"]
18
- s.add_dependency('rgen', '>= 0.6.0')
18
+ s.add_dependency('rgen', '>= 0.6.1')
19
19
  gemfiles = Rake::FileList.new
20
20
  gemfiles.include("{lib,test}/**/*")
21
21
  gemfiles.include(DocFiles)
22
22
  gemfiles.include("Rakefile")
23
23
  gemfiles.exclude(/\b\.bak\b/)
24
24
  s.files = gemfiles
25
- s.rdoc_options = ["--main", "README", "-x", "test"]
25
+ s.rdoc_options = ["--main", "README.rdoc", "-x", "test"]
26
26
  s.extra_rdoc_files = DocFiles
27
27
  end
28
28
 
29
- Rake::RDocTask.new do |rd|
30
- rd.main = "README"
29
+ RDoc::Task.new do |rd|
30
+ rd.main = "README.rdoc"
31
31
  rd.rdoc_files.include(DocFiles)
32
32
  rd.rdoc_files.include("lib/**/*.rb")
33
33
  rd.rdoc_dir = "doc"
34
34
  end
35
35
 
36
- RTextPackageTask = Rake::GemPackageTask.new(RTextGemSpec) do |p|
36
+ RTextPackageTask = Gem::PackageTask.new(RTextGemSpec) do |p|
37
37
  p.need_zip = false
38
38
  end
39
39
 
@@ -23,6 +23,7 @@ class Completer
23
23
  clazz = context && context.element && context.element.class.ecore
24
24
  if clazz
25
25
  if context.in_block
26
+ # command and lable completion within block
26
27
  types = []
27
28
  labled_refs = []
28
29
  if context.feature
@@ -44,63 +45,68 @@ class Completer
44
45
  end
45
46
  end
46
47
  end
47
- types.uniq.select{|c| c.name.index(context.prefix) == 0}.
48
+ types.uniq.
48
49
  sort{|a,b| a.name <=> b.name}.collect do |c|
49
50
  class_completion_option(c)
50
51
  end +
51
- labled_refs.uniq.select{|r| r.name.index(context.prefix) == 0}.collect do |r|
52
+ labled_refs.uniq.collect do |r|
52
53
  CompletionOption.new("#{r.name}:", "<#{r.eType.name}>")
53
54
  end
54
- else
55
+ elsif !context.problem
56
+ result = []
55
57
  if context.feature
58
+ if context.after_label
59
+ description = "<#{context.feature.eType.name}>"
60
+ else
61
+ description = "[#{context.feature.name}] <#{context.feature.eType.name}>"
62
+ end
56
63
  # value completion
57
64
  if context.feature.is_a?(RGen::ECore::EAttribute) || !context.feature.containment
58
65
  if context.feature.is_a?(RGen::ECore::EReference)
59
66
  if ref_completion_option_provider
60
- ref_completion_option_provider.call(context.feature)
67
+ result += ref_completion_option_provider.call(context.feature)
61
68
  else
62
- []
69
+ # no options
63
70
  end
64
71
  elsif context.feature.eType.is_a?(RGen::ECore::EEnum)
65
- context.feature.eType.eLiterals.collect do |l|
66
- CompletionOption.new("#{l.name}")
72
+ result += context.feature.eType.eLiterals.collect do |l|
73
+ CompletionOption.new("#{l.name}", "<#{context.feature.eType.name}>")
67
74
  end
68
75
  elsif context.feature.eType.instanceClass == String
69
- [ CompletionOption.new("\"\"") ]
76
+ if @lang.unquoted?(context.feature)
77
+ result += [ CompletionOption.new("#{context.feature.name.gsub(/\W/,"")}", description) ]
78
+ else
79
+ result += [ CompletionOption.new("\"\"", description) ]
80
+ end
70
81
  elsif context.feature.eType.instanceClass == Integer
71
- (0..4).collect{|i| CompletionOption.new("#{i}") }
82
+ result += (0..0).collect{|i| CompletionOption.new("#{i}", description) }
72
83
  elsif context.feature.eType.instanceClass == Float
73
- (0..4).collect{|i| CompletionOption.new("#{i}.0") }
84
+ result += (0..0).collect{|i| CompletionOption.new("#{i}.0", description) }
74
85
  elsif context.feature.eType.instanceClass == RGen::MetamodelBuilder::DataTypes::Boolean
75
- [true, false].collect{|b| CompletionOption.new("#{b}") }
86
+ result += [true, false].collect{|b| CompletionOption.new("#{b}", description) }
76
87
  else
77
- []
88
+ # no options
78
89
  end
79
90
  else
80
91
  # containment reference, ignore
81
92
  end
82
- else
83
- result = []
84
- if !@lang.labled_arguments(clazz).any?{|f|
85
- context.element.getGenericAsArray(f.name).size > 0}
86
- result += @lang.unlabled_arguments(clazz).
87
- select{|f| f.name.index(context.prefix) == 0 &&
88
- context.element.getGenericAsArray(f.name).empty?}[0..0].collect do |f|
89
- CompletionOption.new("<#{f.name}>", "<#{f.eType.name}>")
90
- end
91
- end
93
+ end
94
+ if !context.after_label
92
95
  # label completion
93
96
  result += @lang.labled_arguments(clazz).
94
- select{|f| f.name.index(context.prefix) == 0 &&
95
- context.element.getGenericAsArray(f.name).empty?}.collect do |f|
97
+ select{|f|
98
+ !context.element.eIsSet(f.name)}.collect do |f|
96
99
  CompletionOption.new("#{f.name}:", "<#{f.eType.name}>")
97
100
  end
98
- result
99
101
  end
102
+ result
103
+ else
104
+ # missing comma, after curly brace, etc.
105
+ []
100
106
  end
101
107
  elsif context
102
108
  # root classes
103
- @lang.root_classes.select{|c| c.name.index(context.prefix) == 0}.
109
+ @lang.root_classes.
104
110
  sort{|a,b| a.name <=> b.name}.collect do |c|
105
111
  class_completion_option(c)
106
112
  end
@@ -1,4 +1,5 @@
1
1
  require 'rtext/instantiator'
2
+ require 'rtext/tokenizer'
2
3
 
3
4
  module RText
4
5
 
@@ -25,23 +26,33 @@ module RText
25
26
  #
26
27
  module ContextBuilder
27
28
 
28
- Context = Struct.new(:element, :feature, :prefix, :in_array, :in_block)
29
+ Context = Struct.new(:element, :feature, :prefix, :in_array, :in_block, :after_label, :problem)
29
30
 
30
31
  class << self
32
+ include RText::Tokenizer
31
33
 
32
34
  # Builds the context information based on a set of +content_lines+. Content lines
33
35
  # are the RText lines containing the nested command headers in the original order.
34
36
  # The cursor is assumed to be in the last context line at column +position_in_line+
35
37
  def build_context(language, context_lines, position_in_line)
36
- context_info = fix_context(context_lines, position_in_line)
38
+ context_info = fix_context(language, context_lines, position_in_line)
37
39
  return nil unless context_info
38
40
  element = instantiate_context_element(language, context_info)
39
41
  if element
40
- feature = context_info.role &&
41
- element.class.ecore.eAllStructuralFeatures.find{|f| f.name == context_info.role}
42
- Context.new(element, feature, context_info.prefix, context_info.in_array, context_info.in_block)
42
+ after_label = false
43
+ if context_info.role
44
+ if context_info.role.is_a?(Integer)
45
+ feature = language.unlabled_arguments(element.class.ecore)[context_info.role]
46
+ else
47
+ feature = element.class.ecore.eAllStructuralFeatures.find{|f| f.name == context_info.role}
48
+ after_label = true
49
+ end
50
+ else
51
+ feature = nil
52
+ end
53
+ Context.new(element, feature, context_info.prefix, context_info.in_array, context_info.in_block, after_label, context_info.problem)
43
54
  else
44
- Context.new(nil, nil, context_info.prefix, context_info.in_array, context_info.in_block)
55
+ Context.new(nil, nil, context_info.prefix, context_info.in_array, context_info.in_block, false, context_info.problem)
45
56
  end
46
57
  end
47
58
 
@@ -63,82 +74,124 @@ module ContextBuilder
63
74
  def find_leaf_child(element, num_required_children)
64
75
  childs = element.class.ecore.eAllReferences.select{|r| r.containment}.collect{|r|
65
76
  element.getGenericAsArray(r.name)}.flatten
66
- if childs.size > 0
67
- find_leaf_child(childs.first, num_required_children-1)
68
- elsif num_required_children == 0
77
+ if num_required_children == 0
69
78
  element
79
+ elsif childs.size > 0
80
+ find_leaf_child(childs.first, num_required_children-1)
70
81
  else
71
82
  nil
72
83
  end
73
84
  end
74
85
 
75
- ContextInternal = Struct.new(:lines, :num_elements, :role, :prefix, :in_array, :in_block)
86
+ ContextInternal = Struct.new(:lines, :num_elements, :role, :prefix, :in_array, :in_block, :problem)
76
87
 
77
88
  # extend +context_lines+ into a set of lines which can be processed by the RText
78
- def fix_context(context_lines, position_in_line)
89
+ def fix_context(language, context_lines, position_in_line)
79
90
  context_lines = context_lines.dup
80
91
  # make sure there is at least one line
81
- # the frontent may ommit the last context line if the cursor is at collumn 0
82
- if context_lines.empty? || (position_in_line == 0 && context_lines.last != "")
92
+ if context_lines.empty?
83
93
  context_lines << ""
84
94
  end
85
95
  position_in_line ||= context_lines.last.size
86
96
  # cut off last line right of cursor
87
- context_lines << context_lines.pop[0..position_in_line-1]
97
+ if position_in_line < 1
98
+ context_lines.pop
99
+ context_lines << ""
100
+ else
101
+ context_lines << context_lines.pop[0..position_in_line-1]
102
+ end
103
+ problem = nil
88
104
  line = context_lines.last
89
- if line =~ /^\s*\w+\s+/
90
- # this line contains a new element
91
- num_elements = 1
92
- in_block = false
93
- # labled array value
94
- if line =~ /\W(\w+):\s*\[([^\]]*)$/
95
- role = $1
96
- array_content = $2
97
- in_array = true
98
- if array_content =~ /,\s*(\S*)$/
99
- prefix = $1
100
- line.sub!(/,\s*\S*$/, "]")
101
- else
102
- array_content =~ /\s*(\S*)$/
103
- prefix = $1
104
- line.sub!(/\[[^\]]*$/, "[]")
105
- end
106
- # labled value
107
- elsif line =~ /\W(\w+):\s*(\S*)$/
108
- role = $1
109
- prefix = $2
105
+ if line =~ /\{\s*$/
106
+ # remove curly brace from last line, required for correct counting of num_elements
107
+ line.sub!(/\{\s*$/,"")
108
+ problem = :after_curly
109
+ end
110
+
111
+ num_elements = in_block = in_array = missing_comma = role = prefix = nil
112
+ tokens = tokenize(line, language.reference_regexp)
113
+ tokens.pop if tokens.last && tokens.last.kind == :newline
114
+ if tokens.size > 0 && tokens[0].kind == :identifier
115
+ if tokens.size > 1 || line =~ /\s+$/
116
+ # this line contains a new element
117
+ num_elements = 1
118
+ in_block = false
110
119
  in_array = false
111
- line.sub!(/\s*\w+:\s*\S*$/, "")
112
- line.sub!(/,$/, "")
113
- # unlabled value or label
114
- elsif line =~ /[,\s](\S*)$/
115
120
  role = nil
116
- prefix = $1
117
- in_array = false
118
- line.sub!(/\s*\S*$/, "")
119
- line.sub!(/,$/, "")
120
- # TODO: unlabled array value
121
- else
122
- # parse problem
123
- return nil
124
- end
125
- else
126
- # this line is in the content block
127
- num_elements = 0
128
- in_block = true
129
- # role or new element
130
- if line =~ /^\s*(\w*)$/
131
- prefix = $1
121
+ missing_comma = false
122
+ unlabled_index = 0
123
+ tokens[1..-1].each do |token|
124
+ if token.kind == "["
125
+ in_array = true
126
+ elsif token.kind == "]"
127
+ in_array = false
128
+ missing_comma = true
129
+ role = nil
130
+ elsif token.kind == :label
131
+ role = token.value.sub(/:$/, "")
132
+ elsif token.kind == ","
133
+ missing_comma = false
134
+ role = nil unless in_array
135
+ unlabled_index += 1 unless in_array
136
+ end
137
+ end
138
+ if ((tokens.size == 1 && line =~ /\s+$/) ||
139
+ tokens.last.kind == "," ||
140
+ in_array ||
141
+ ([:error, :string, :integer, :float, :boolean, :identifier, :reference].
142
+ include?(tokens.last.kind) && line !~ /\s$/)) &&
143
+ !tokens.any?{|t| t.kind == :label} &&
144
+ !(problem == :after_curly)
145
+ role ||= unlabled_index
146
+ end
147
+ if [:string, :integer, :float, :boolean, :identifier, :reference].
148
+ include?(tokens.last.kind) && line =~ /\s$/ && tokens.size > 1
149
+ missing_comma = true
150
+ role = nil unless in_array
151
+ end
152
+ if [:error, :string, :integer, :float, :boolean, :identifier, :reference].
153
+ include?(tokens.last.kind) && line !~ /\s$/
154
+ prefix = tokens.last.value.to_s
155
+ else
156
+ prefix = ""
157
+ end
158
+ else
159
+ # in completion of command
160
+ num_elements = 0
161
+ missing_comma = false
162
+ in_block = (context_lines.size > 1)
163
+ prefix = tokens[0].value.to_s
132
164
  role, in_array = find_role(context_lines[0..-2])
133
165
  # fix single role lable
134
166
  if context_lines[-2] =~ /^\s*\w+:\s*$/
135
167
  context_lines[-1] = context_lines.pop
136
168
  end
137
- else
138
- # comment, closing brackets, etc.
139
- return nil
140
169
  end
170
+ elsif line.strip.empty?
171
+ # in completion of command but without prefix
172
+ num_elements = 0
173
+ missing_comma = false
174
+ in_block = (context_lines.size > 1)
175
+ prefix = ""
176
+ role, in_array = find_role(context_lines[0..-2])
177
+ # fix single role lable
178
+ if context_lines[-2] =~ /^\s*\w+:\s*$/
179
+ context_lines[-1] = context_lines.pop
180
+ end
181
+ else
182
+ # comment, closing brackets, etc.
183
+ num_elements = 0
184
+ missing_comma = false
185
+ in_block = (context_lines.size > 1)
186
+ return nil
141
187
  end
188
+
189
+ # remove prefix, a value which is currently being completed should not be part of the
190
+ # context model
191
+ if prefix && prefix.size > 0
192
+ line.slice!(-(prefix.size)..-1)
193
+ end
194
+
142
195
  context_lines.reverse.each do |l|
143
196
  if l =~ /\{\s*$/
144
197
  context_lines << "}"
@@ -147,7 +200,8 @@ module ContextBuilder
147
200
  context_lines << "]"
148
201
  end
149
202
  end
150
- ContextInternal.new(context_lines, num_elements, role, prefix, in_array, in_block)
203
+ problem = :missing_comma if !problem && missing_comma
204
+ ContextInternal.new(context_lines, num_elements, role, prefix, in_array, in_block, problem)
151
205
  end
152
206
 
153
207
  def find_role(context_lines)
@@ -36,10 +36,13 @@ class DefaultLoader
36
36
  def initialize(language, fragmented_model, options={})
37
37
  @lang = language
38
38
  @model = fragmented_model
39
+ @files_added = []
40
+ @files_changed = []
41
+ @files_removed = []
39
42
  @change_detector = RGen::Util::FileChangeDetector.new(
40
- :file_added => method(:file_added),
41
- :file_removed => method(:file_removed),
42
- :file_changed => method(:file_changed))
43
+ :file_added => lambda {|f| @files_added << f },
44
+ :file_removed => lambda {|f| @files_removed << f},
45
+ :file_changed => lambda {|f| @files_changed << f})
43
46
  @cache = options[:cache]
44
47
  @fragment_by_file = {}
45
48
  pattern = options[:pattern]
@@ -61,21 +64,75 @@ class DefaultLoader
61
64
  # optionally, the proc may take second argument which is the overall number of files
62
65
  # default: no after load proc
63
66
  #
67
+ # :on_progress
68
+ # a proc which is called when some progress is made
69
+ # receives the current fragment being loaded, the actual work done as an integer and
70
+ # the overall work to be done as an integer
71
+ # default: no on progress proc
72
+ #
64
73
  def load(options={})
65
74
  @before_load_proc = options[:before_load]
66
75
  @after_load_proc = options[:after_load]
67
76
  files = @file_provider.call
68
77
  @num_files = files.size
78
+ @files_added = []
79
+ @files_changed = []
80
+ @files_removed = []
69
81
  @change_detector.check_files(files)
82
+ @progress_monitor = ProgressMonitor.new(options[:on_progress], @files_added + @files_changed)
83
+ @files_added.each {|f| file_added(f)}
84
+ @files_changed.each {|f| file_changed(f)}
85
+ @files_removed.each {|f| file_removed(f)}
86
+ @lang.reference_qualifier.call(@model.unresolved_refs, @model)
70
87
  @model.resolve(:fragment_provider => method(:fragment_provider),
71
88
  :use_target_type => @lang.per_type_identifier)
72
89
  end
73
90
 
74
91
  private
75
92
 
93
+ class ProgressMonitor
94
+ def initialize(on_progress_proc, files)
95
+ @on_progress_proc = on_progress_proc || lambda {|frag, work_done, work_overall| }
96
+ # there is a progress call twice for each element (in tokenizer and instantiator)
97
+ @work_overall = num_elements(files)*2
98
+ @work_done = 0
99
+ @work_last_sent = 0
100
+ end
101
+
102
+ def before_fragment_load(frag, kind)
103
+ @loading_cached = (kind == :load_cached)
104
+ end
105
+
106
+ def after_fragment_load(frag)
107
+ @work_done += frag.elements.size*2 if @loading_cached
108
+ @on_progress_proc.call(frag, @work_done, @work_overall)
109
+ @work_last_sent = @work_done
110
+ end
111
+
112
+ def instantiator_progress(frag)
113
+ @work_done += 1
114
+ if @work_done > @work_last_sent + 100
115
+ @on_progress_proc.call(frag, @work_done, @work_overall)
116
+ @work_last_sent = @work_done
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def num_elements(files)
123
+ result = 0
124
+ files.each do |f|
125
+ content = File.open(f, "rb"){|fh| fh.read}
126
+ result += content.scan(/\n\s*\w+\s+/).size
127
+ result += 1 if content =~ /^\s*\w+\s+/
128
+ end
129
+ result
130
+ end
131
+ end
132
+
76
133
  def file_added(file)
77
134
  fragment = RGen::Fragment::ModelFragment.new(file,
78
- :identifier_provider => @lang.identifier_provider)
135
+ :identifier_provider => lambda {|e, c| @lang.identifier_provider.call(e, nil, nil, nil)})
79
136
  load_fragment_cached(fragment)
80
137
  @model.add_fragment(fragment)
81
138
  @fragment_by_file[file] = fragment
@@ -88,7 +145,7 @@ class DefaultLoader
88
145
 
89
146
  def file_changed(file)
90
147
  fragment = RGen::Fragment::ModelFragment.new(file,
91
- :identifier_provider => @lang.identifier_provider)
148
+ :identifier_provider => lambda {|e, c| @lang.identifier_provider.call(e, nil, nil, nil)})
92
149
  load_fragment_cached(fragment)
93
150
  if @dont_reload_with_errors && fragment.data[:problems].size > 0
94
151
  # keep old fragment but attach new problems
@@ -135,6 +192,7 @@ class DefaultLoader
135
192
  end
136
193
 
137
194
  def call_before_load_proc(fragment, kind)
195
+ @progress_monitor.before_fragment_load(fragment, kind)
138
196
  if @before_load_proc
139
197
  if @before_load_proc.arity == 3
140
198
  @before_load_proc.call(fragment, kind, @num_files)
@@ -145,6 +203,7 @@ class DefaultLoader
145
203
  end
146
204
 
147
205
  def call_after_load_proc(fragment)
206
+ @progress_monitor.after_fragment_load(fragment)
148
207
  if @after_load_proc
149
208
  if @after_load_proc.arity == 2
150
209
  @after_load_proc.call(fragment, @num_files)
@@ -160,20 +219,26 @@ class DefaultLoader
160
219
  problems = []
161
220
  root_elements = []
162
221
  inst = RText::Instantiator.new(@lang)
163
- File.open(fragment.location) do |f|
222
+ File.open(fragment.location, "rb") do |f|
164
223
  inst.instantiate(f.read,
165
224
  :env => env,
166
225
  :unresolved_refs => urefs,
167
226
  :problems => problems,
168
227
  :root_elements => root_elements,
169
228
  :fragment_ref => fragment.fragment_ref,
170
- :file_name => fragment.location)
229
+ :file_name => fragment.location,
230
+ :on_progress => lambda do
231
+ @progress_monitor.instantiator_progress(fragment)
232
+ end)
171
233
  end
172
- fragment.data = {:problems => problems}
234
+ # data might have been created during instantiation (e.g. comment or annotation handler)
235
+ fragment.data ||= {}
236
+ fragment.data[:problems] = problems
173
237
  fragment.set_root_elements(root_elements,
174
238
  :unresolved_refs => urefs,
175
239
  :elements => env.elements)
176
240
  fragment.build_index
241
+ @lang.reference_qualifier.call(urefs, fragment)
177
242
  fragment.resolve_local(:use_target_type => @lang.per_type_identifier)
178
243
  end
179
244