neo_doxygen: Add an option to remove the `def` keyword of Python.
[nit.git] / contrib / neo_doxygen / src / neo_doxygen.nit
index f4c0f50..431809c 100644 (file)
@@ -20,16 +20,24 @@ module neo_doxygen
 
 import model
 import doxml
+import graph_store
 import console
 import opts
 
 # An importation task.
 class NeoDoxygenJob
-       var client: Neo4jClient
+
+       # The storage medium to use.
+       var store: GraphStore
+
+       # The loaded project graph.
        var model: ProjectGraph is noinit
 
-       # How many operation can be executed in one batch?
-       private var batch_max_size = 1000
+       # Escape control sequence to save the cursor position.
+       private var term_save_cursor: String = (new TermSaveCursor).to_s
+
+       # Escape control sequence to rewind to the last saved cursor position.
+       private var term_rewind: String = "{new TermRestoreCursor}{new TermEraseDisplayDown}"
 
        # Generate a graph from the specified project model.
        #
@@ -41,27 +49,44 @@ class NeoDoxygenJob
        fun load_project(name: String, dir: String, source: SourceLanguage) do
                check_name name
                model = new ProjectGraph(name)
-               # TODO Let the user select the language.
                var reader = new CompoundFileReader(model, source)
                # Queue for sub-directories.
                var directories = new Array[String]
+               var file_count = 0
 
-               if dir.length > 1 and dir.chars.last == "/" then
-                       dir = dir.substring(0, dir.length - 1)
+               if dir == "" then
+                       printn "Reading the current directory... "
+               else
+                       printn "Reading {dir}... "
                end
                loop
-                       for f in dir.files do
+                       for f in list_files(dir) do
                                var path = dir/f
                                if path.file_stat.is_dir then
                                        directories.push(path)
                                else if f.has_suffix(".xml") and f != "index.xml" then
-                                       print "Processing {path}..."
                                        reader.read(path)
+                                       file_count += 1
                                end
                        end
                        if directories.length <= 0 then break
                        dir = directories.pop
                end
+               model.add_global_modules
+               print "Done."
+               if file_count < 2 then
+                       print "{file_count} file read."
+               else
+                       print "{file_count} files read."
+               end
+       end
+
+       # List files in a directory.
+       #
+       # This method may be redefined to force the order in which the files
+       # are read by `load_project`.
+       protected fun list_files(dir: String): Collection[String] do
+               return dir.files
        end
 
        # Check the project’s name.
@@ -70,11 +95,7 @@ class NeoDoxygenJob
                        sys.stderr.write("{sys.program_name}: The project’s name must not" +
                                        " begin with an upper case letter. Got `{name}`.\n")
                end
-               var query = new CypherQuery.from_string("match n where \{name\} in labels(n) return count(n)")
-               query.params["name"] = name
-               var data = client.cypher(query).as(JsonObject)["data"]
-               var result = data.as(JsonArray).first.as(JsonArray).first.as(Int)
-               assert name_unused: result == 0 else
+               assert name_unused: not store.has_node_label(name) else
                        sys.stderr.write("{sys.program_name}: The label `{name}` is already" +
                        " used in the specified graph.\n")
                end
@@ -82,46 +103,15 @@ class NeoDoxygenJob
 
        # Save the graph.
        fun save do
+               sys.stdout.write "Linking nodes...{term_save_cursor} "
                model.put_edges
+               print "{term_rewind} Done."
                var nodes = model.all_nodes
-               print("Saving {nodes.length} nodes...")
-               push_all(nodes)
+               sys.stdout.write "Saving {nodes.length} nodes..."
+               store.save_all(nodes)
                var edges = model.all_edges
-               print("Saving {edges.length} edges...")
-               push_all(edges)
-       end
-
-       # Save `neo_entities` in the database using batch mode.
-       private fun push_all(neo_entities: Collection[NeoEntity]) do
-               var batch = new NeoBatch(client)
-               var len = neo_entities.length
-               var sum = 0
-               var i = 1
-
-               for nentity in neo_entities do
-                       batch.save_entity(nentity)
-                       if i == batch_max_size then
-                               do_batch(batch)
-                               sum += batch_max_size
-                               print("\t{sum * 100 / len}% done.")
-                               batch = new NeoBatch(client)
-                               i = 1
-                       else
-                               i += 1
-                       end
-               end
-               do_batch(batch)
-       end
-
-       # Execute `batch` and check for errors.
-       #
-       # Abort if `batch.execute` returns errors.
-       private fun do_batch(batch: NeoBatch) do
-               var errors = batch.execute
-               if not errors.is_empty then
-                       for e in errors do sys.stderr.write("{sys.program_name}: {e}\n")
-                       exit(1)
-               end
+               sys.stdout.write "Saving {edges.length} edges..."
+               store.save_all(edges)
        end
 end
 
@@ -159,6 +149,7 @@ class NeoDoxygenCommand
        init do
                sources["any"] = new DefaultSource
                sources["java"] = new JavaSource
+               sources["python"] = new PythonSource
 
                var prefix = new OptionText("""
 {{{"NAME".bold}}}
@@ -189,15 +180,15 @@ class NeoDoxygenCommand
                opt_dest.default_value = default_dest
                option_context.add_option(opt_dest)
 
+               opt_help = new OptionBool("Show the help (this page).",
+                               "-h", "--help")
+               option_context.add_option(opt_help)
+
                var keys = new Array[String].from(sources.keys)
                opt_src_lang = new OptionEnum(keys,
-                               "The programming language to assume when processing chunk in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.",
+                               "The programming language to assume when processing chunks in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.",
                                keys.index_of("any"), "--src-lang")
                option_context.add_option(opt_src_lang)
-
-               opt_help = new OptionBool("Show the help (this page).",
-                               "-h", "--help")
-               option_context.add_option(opt_help)
        end
 
        # Start the application.
@@ -228,13 +219,18 @@ class NeoDoxygenCommand
                var dest = opt_dest.value
                var project_name = rest[0]
                var dir = rest[1]
-               var neo = new NeoDoxygenJob(new Neo4jClient(dest or else default_dest))
+               var neo = new NeoDoxygenJob(create_store(dest or else default_dest))
 
                neo.load_project(project_name, dir, source)
                neo.save
                return 0
        end
 
+       # Create an instance of `GraphStore` for the specified destination.
+       protected fun create_store(dest: String): GraphStore do
+               return new Neo4jStore(new Neo4jClient(dest))
+       end
+
        # Show the help.
        fun show_help do
                option_context.usage
@@ -254,8 +250,8 @@ end
 
 # Add handling of multi-line descriptions.
 #
-# Note: The algorithm is naive and do not handle internationalisation and
-# escape sequences.
+# Note: The algorithm is naive and do not handle internationalisation,
+# multi-byte characters and control characters.
 redef class Option
 
        redef fun pretty(off) do