src/indexing: introduce `code_index`
[nit.git] / src / indexing / code_index.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # An index that contains Nit code
16 #
17 # Model entities are indexed by their ANode.
18 #
19 # Vectorization is based on model usage such as:
20 # * modules importation
21 # * classes spcialization and refinement
22 # * methods calls and refinements
23 #
24 # Example:
25 # ~~~nitish
26 # # Create the index
27 # var index = new CodeIndex(toolcontext)
28 # for mentity in mentities do
29 # index.index_mentity(mentity)
30 # end
31 #
32 # # Match a piece of code
33 # var matches = index.match_code("print \"Hello, World!\"")
34 # for match in matches do
35 # print match
36 # end
37 # ~~~
38 module code_index
39
40 import vsm
41 import semantize
42 import parser_util
43
44 # Index for Nit doc
45 class CodeIndex
46 super VSMIndex
47
48 redef type DOC: CodeDocument
49
50 # ToolContext used to parse pieces of code
51 var toolcontext: ToolContext
52
53 # Index `mentity`
54 fun index_mentity(mentity: MEntity) do
55 var terms = vectorize_mentity(mentity)
56 var doc = new CodeDocument(mentity, terms)
57 index_document(doc, false)
58 end
59
60 # Match `code` with the index
61 fun match_code(code: String): Array[IndexMatch[DOC]] do
62 var node = parse_code(code)
63 if node == null then return new Array[IndexMatch[DOC]]
64 return match_node(node)
65 end
66
67 # Match `node` with the index
68 fun match_node(node: ANode): Array[IndexMatch[DOC]] do
69 var vector = vectorize_node(node)
70 return match_vector(vector)
71 end
72
73 # Parse a piece of code
74 private fun parse_code(code: String): nullable AModule do
75 # Try to parse code
76 var node = toolcontext.parse_something(code)
77 if not node isa AModule then return null
78
79 # Load code into model
80 var mbuilder = toolcontext.modelbuilder
81 mbuilder.load_rt_module(null, node, "tmp")
82 mbuilder.run_phases
83 return node
84 end
85
86 # Transform `node` in a Vector
87 private fun vectorize_node(node: ANode): Vector do
88 var visitor = new CodeIndexVisitor
89 visitor.enter_visit(node)
90 return visitor.vector
91 end
92
93 # Transform `mentity` in a Vector
94 private fun vectorize_mentity(mentity: MEntity): Vector do
95 var node = toolcontext.modelbuilder.mentity2node(mentity)
96 if node == null then return new Vector
97 return vectorize_node(node)
98 end
99 end
100
101 # A specific document for mentities code
102 class CodeDocument
103 super Document
104 autoinit(mentity, terms_count)
105
106 # MEntity related to this document
107 var mentity: MEntity
108
109 redef var title = mentity.full_name is lazy
110
111 redef var uri = mentity.location.to_s is lazy
112 end
113
114 # Code index visitor
115 #
116 # Used to build a VSM Vector from a Nit ANode.
117 private class CodeIndexVisitor
118 super Visitor
119
120 var vector = new Vector
121
122 redef fun visit(node) do
123 node.accept_code_index_visitor(self)
124 end
125 end
126
127 redef class ANode
128 private fun accept_code_index_visitor(v: CodeIndexVisitor) do
129 visit_all(v)
130 end
131 end
132
133 redef class AStdImport
134 redef fun accept_code_index_visitor(v) do
135 var mmodule = self.mmodule
136 if mmodule != null then
137 v.vector.inc "import#{mmodule.full_name}"
138 end
139 end
140 end
141
142 redef class AStdClassdef
143 redef fun accept_code_index_visitor(v) do
144 var mclassdef = self.mclassdef
145 if mclassdef != null then
146 if not mclassdef.is_intro then
147 v.vector.inc "redef#{mclassdef.full_name}"
148 v.vector.inc "redef#{mclassdef.mclass.full_name}"
149 end
150 end
151 visit_all(v)
152 end
153 end
154
155 redef class ASuperPropdef
156 redef fun accept_code_index_visitor(v) do
157 var mtype = self.n_type.mtype
158 if mtype isa MClassType then
159 v.vector.inc "super#{mtype.mclass.intro.full_name}"
160 v.vector.inc "super#{mtype.mclass.full_name}"
161 end
162 end
163 end
164
165 redef class APropdef
166 redef fun accept_code_index_visitor(v) do
167 var mpropdef = self.mpropdef
168 if mpropdef != null then
169 if not mpropdef.is_intro then
170 v.vector.inc "redef#{mpropdef.mproperty.intro.full_name}"
171 v.vector.inc "redef#{mpropdef.mproperty.full_name}"
172 end
173 end
174 visit_all(v)
175 end
176 end
177
178 redef class ASendExpr
179 redef fun accept_code_index_visitor(v) do
180 var callsite = self.callsite
181 if callsite != null then
182 var args = callsite.signaturemap.as(not null).map.length
183 v.vector.inc "call#{callsite.mpropdef.full_name}({args})"
184 v.vector.inc "call#{callsite.mpropdef.mproperty.full_name}({args})"
185 v.vector.inc "call#{callsite.mpropdef.mclassdef.full_name}({args})"
186 v.vector.inc "call#{callsite.mpropdef.mclassdef.mclass.full_name}({args})"
187 end
188 visit_all(v)
189 end
190 end
191
192 redef class ANewExpr
193 redef fun accept_code_index_visitor(v) do
194 var callsite = self.callsite
195 if callsite != null then
196 var args = callsite.signaturemap.as(not null).map.length
197 v.vector.inc "call#{callsite.mpropdef.full_name}({args})"
198 v.vector.inc "call#{callsite.mpropdef.mproperty.full_name}({args})"
199 v.vector.inc "new#{callsite.mpropdef.mclassdef.full_name}({args})"
200 v.vector.inc "new#{callsite.mpropdef.mclassdef.mclass.full_name}({args})"
201 end
202 visit_all(v)
203 end
204 end