--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# An index that contains Nit code
+#
+# Model entities are indexed by their ANode.
+#
+# Vectorization is based on model usage such as:
+# * modules importation
+# * classes spcialization and refinement
+# * methods calls and refinements
+#
+# Example:
+# ~~~nitish
+# # Create the index
+# var index = new CodeIndex(toolcontext)
+# for mentity in mentities do
+# index.index_mentity(mentity)
+# end
+#
+# # Match a piece of code
+# var matches = index.match_code("print \"Hello, World!\"")
+# for match in matches do
+# print match
+# end
+# ~~~
+module code_index
+
+import vsm
+import semantize
+import parser_util
+
+# Index for Nit doc
+class CodeIndex
+ super VSMIndex
+
+ redef type DOC: CodeDocument
+
+ # ToolContext used to parse pieces of code
+ var toolcontext: ToolContext
+
+ # Index `mentity`
+ fun index_mentity(mentity: MEntity) do
+ var terms = vectorize_mentity(mentity)
+ var doc = new CodeDocument(mentity, terms)
+ index_document(doc, false)
+ end
+
+ # Match `code` with the index
+ fun match_code(code: String): Array[IndexMatch[DOC]] do
+ var node = parse_code(code)
+ if node == null then return new Array[IndexMatch[DOC]]
+ return match_node(node)
+ end
+
+ # Match `node` with the index
+ fun match_node(node: ANode): Array[IndexMatch[DOC]] do
+ var vector = vectorize_node(node)
+ return match_vector(vector)
+ end
+
+ # Parse a piece of code
+ private fun parse_code(code: String): nullable AModule do
+ # Try to parse code
+ var node = toolcontext.parse_something(code)
+ if not node isa AModule then return null
+
+ # Load code into model
+ var mbuilder = toolcontext.modelbuilder
+ mbuilder.load_rt_module(null, node, "tmp")
+ mbuilder.run_phases
+ return node
+ end
+
+ # Transform `node` in a Vector
+ private fun vectorize_node(node: ANode): Vector do
+ var visitor = new CodeIndexVisitor
+ visitor.enter_visit(node)
+ return visitor.vector
+ end
+
+ # Transform `mentity` in a Vector
+ private fun vectorize_mentity(mentity: MEntity): Vector do
+ var node = toolcontext.modelbuilder.mentity2node(mentity)
+ if node == null then return new Vector
+ return vectorize_node(node)
+ end
+end
+
+# A specific document for mentities code
+class CodeDocument
+ super Document
+ autoinit(mentity, terms_count)
+
+ # MEntity related to this document
+ var mentity: MEntity
+
+ redef var title = mentity.full_name is lazy
+
+ redef var uri = mentity.location.to_s is lazy
+end
+
+# Code index visitor
+#
+# Used to build a VSM Vector from a Nit ANode.
+private class CodeIndexVisitor
+ super Visitor
+
+ var vector = new Vector
+
+ redef fun visit(node) do
+ node.accept_code_index_visitor(self)
+ end
+end
+
+redef class ANode
+ private fun accept_code_index_visitor(v: CodeIndexVisitor) do
+ visit_all(v)
+ end
+end
+
+redef class AStdImport
+ redef fun accept_code_index_visitor(v) do
+ var mmodule = self.mmodule
+ if mmodule != null then
+ v.vector.inc "import#{mmodule.full_name}"
+ end
+ end
+end
+
+redef class AStdClassdef
+ redef fun accept_code_index_visitor(v) do
+ var mclassdef = self.mclassdef
+ if mclassdef != null then
+ if not mclassdef.is_intro then
+ v.vector.inc "redef#{mclassdef.full_name}"
+ v.vector.inc "redef#{mclassdef.mclass.full_name}"
+ end
+ end
+ visit_all(v)
+ end
+end
+
+redef class ASuperPropdef
+ redef fun accept_code_index_visitor(v) do
+ var mtype = self.n_type.mtype
+ if mtype isa MClassType then
+ v.vector.inc "super#{mtype.mclass.intro.full_name}"
+ v.vector.inc "super#{mtype.mclass.full_name}"
+ end
+ end
+end
+
+redef class APropdef
+ redef fun accept_code_index_visitor(v) do
+ var mpropdef = self.mpropdef
+ if mpropdef != null then
+ if not mpropdef.is_intro then
+ v.vector.inc "redef#{mpropdef.mproperty.intro.full_name}"
+ v.vector.inc "redef#{mpropdef.mproperty.full_name}"
+ end
+ end
+ visit_all(v)
+ end
+end
+
+redef class ASendExpr
+ redef fun accept_code_index_visitor(v) do
+ var callsite = self.callsite
+ if callsite != null then
+ var args = callsite.signaturemap.as(not null).map.length
+ v.vector.inc "call#{callsite.mpropdef.full_name}({args})"
+ v.vector.inc "call#{callsite.mpropdef.mproperty.full_name}({args})"
+ v.vector.inc "call#{callsite.mpropdef.mclassdef.full_name}({args})"
+ v.vector.inc "call#{callsite.mpropdef.mclassdef.mclass.full_name}({args})"
+ end
+ visit_all(v)
+ end
+end
+
+redef class ANewExpr
+ redef fun accept_code_index_visitor(v) do
+ var callsite = self.callsite
+ if callsite != null then
+ var args = callsite.signaturemap.as(not null).map.length
+ v.vector.inc "call#{callsite.mpropdef.full_name}({args})"
+ v.vector.inc "call#{callsite.mpropdef.mproperty.full_name}({args})"
+ v.vector.inc "new#{callsite.mpropdef.mclassdef.full_name}({args})"
+ v.vector.inc "new#{callsite.mpropdef.mclassdef.mclass.full_name}({args})"
+ end
+ visit_all(v)
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_code_index is test
+
+import code_index
+import frontend
+
+class TestCodeIndex
+ test
+
+ # CodeIndex used in tests
+ var test_index: CodeIndex is noinit
+
+ # Initialize test variables
+ #
+ # Must be called before test execution.
+ # FIXME should be before_all
+ fun build_test_env is before do
+ var test_path = "NIT_TESTING_PATH".environ.dirname
+ var test_src = test_path / "../../../tests/test_prog"
+
+ # build model
+ var toolcontext = new ToolContext
+ var model = new Model
+ var modelbuilder = new ModelBuilder(model, toolcontext)
+ var mmodules = modelbuilder.parse_full([test_src])
+ modelbuilder.run_phases
+ toolcontext.run_global_phases(mmodules)
+
+ # create index
+ var index = new CodeIndex(toolcontext)
+ for mmodule in mmodules do
+ index.index_mentity(mmodule)
+ end
+ test_index = index
+ modelbuilder.paths.add test_src
+ end
+
+ fun test_find1 is test do
+ var query = "import game\n"
+ var matches = test_index.match_code(query)
+ assert matches.first.document.mentity.full_name == "test_prog::test_prog"
+ end
+
+ fun test_find2 is test do
+ var query = "import game\nimport rpg\n"
+ var matches = test_index.match_code(query)
+ assert matches.first.document.mentity.full_name == "test_prog::game"
+ end
+
+ fun test_find3 is test do
+ var query = "import game\nclass MyGame\nsuper Game\nredef fun start_game do end\nend\n"
+ var matches = test_index.match_code(query)
+ assert matches.first.document.mentity.full_name == "test_prog::game_examples"
+ end
+
+ fun test_find_error is test do
+ var query = "error"
+ var matches = test_index.match_code(query)
+ assert matches.is_empty
+ end
+end