rtext 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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