doc/commands: introduce `commands_main`, commands about main programs
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 2 May 2018 23:17:26 +0000 (19:17 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Thu, 10 May 2018 20:35:10 +0000 (16:35 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/doc/commands/commands_main.nit [new file with mode: 0644]
src/doc/commands/tests/test_commands_main.nit [new file with mode: 0644]

diff --git a/src/doc/commands/commands_main.nit b/src/doc/commands/commands_main.nit
new file mode 100644 (file)
index 0000000..851b79e
--- /dev/null
@@ -0,0 +1,335 @@
+# 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 commands_main
+
+import doc::commands::commands_model
+
+# Cmd that finds the mains of an `mentity`
+class CmdMains
+       super CmdEntityList
+
+       redef fun init_results do
+               if results != null then return new CmdSuccess
+
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+               var mentities = new Array[MEntity]
+
+               if mentity isa MPackage then
+                       for mmodule in mentity.collect_all_mmodules(view) do
+                               if mmodule_has_main(mmodule) then mentities.add mmodule
+                       end
+               else if mentity isa MGroup then
+                       for mmodule in mentity.collect_all_mmodules(view) do
+                               if mmodule_has_main(mmodule) then mentities.add mmodule
+                       end
+               else if mentity isa MModule then
+                       if mmodule_has_main(mentity) then mentities.add mentity
+               else
+                       return new WarningNoMains(mentity)
+               end
+
+               if mentities.is_empty then return new WarningNoMains(mentity)
+
+               self.results = mentities
+               return res
+       end
+
+       # Does `mmodule` has a `main` method?
+       private fun mmodule_has_main(mmodule: MModule): Bool do
+               for mclassdef in mmodule.collect_redef_mclassdefs(view) do
+                       if mclassdef.name != "Sys" then continue
+                       for mpropdef in mclassdef.collect_redef_mpropdefs(view) do
+                               if mpropdef.name == "main" then return true
+                       end
+               end
+               return false
+       end
+end
+
+# No tests for `mentity`
+class WarningNoMains
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No main found for `{mentity.full_name}`"
+end
+
+# Cmd that finds the nitc command related to an `mentity`
+class CmdMainCompile
+       super CmdEntity
+
+       var file: nullable SourceFile = null
+
+       var command: nullable String is lazy do
+               var path = test_path(file)
+               if path == null then return null
+               return "nitc {path}"
+       end
+
+       redef fun init_command do
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+
+               if mentity isa MModule then
+                       file = mmodule_main(mentity)
+                       if file == null then return new WarningNoMain(mentity)
+               else
+                       return new WarningNoMain(mentity)
+               end
+               return res
+       end
+
+       # Does `mmodule` has a `main` method?
+       private fun mmodule_main(mmodule: MModule): nullable SourceFile do
+               for mclassdef in mmodule.mclassdefs do
+                       if mclassdef.name != "Sys" then continue
+                       for mpropdef in mclassdef.mpropdefs do
+                               if mpropdef.name == "main" then return mmodule.location.file
+                       end
+               end
+               return null
+       end
+
+       # Return the sourcefile path
+       #
+       # This method exists for the only purpose to be redefined by nitunit tests
+       # to avoid path diffs.
+       private fun test_path(file: nullable SourceFile): nullable String do
+               if file == null then return null
+               var base_path = mentity.as(MModule).mpackage.as(not null).location.file.as(not null).filename
+               return file.filename.replace(base_path, "")
+       end
+end
+
+# No tests for `mentity`
+class WarningNoMain
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No main to compile for `{mentity.full_name}`"
+end
+
+# Cmd that finds the nitunit command related to an `mentity`
+class CmdTesting
+       super CmdEntityList
+
+       var command: nullable String is lazy do
+               var results = self.results
+               if results == null then return null
+
+               var tpl = new Template
+               tpl.add "nitunit"
+               for result in results do
+                       var path = test_path(result)
+                       if path == null then continue
+                       tpl.add " {path}"
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun init_results do
+               if results != null then return new CmdSuccess
+
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+
+               var mentities = new Array[MEntity]
+               if not mentity isa MPackage then return new WarningNoTest(mentity)
+
+               for mgroup in mentity.collect_all_mgroups(view) do
+                       if mgroup.is_test then
+                               mentities.add mgroup
+                               continue
+                       end
+                       for mmodule in mgroup.collect_mmodules(view) do
+                               if mmodule.is_test then mentities.add mmodule
+                       end
+               end
+
+               if mentities.is_empty then return new WarningNoTest(mentity)
+
+               self.results = mentities
+               return res
+       end
+
+       # Return the mentity path
+       #
+       # This method exists for the only purpose to be redefined by nitunit tests
+       # to avoid path diffs.
+       private fun test_path(mentity: MEntity): nullable String do
+               var file = mentity.location.file
+               if file == null then return null
+               var base_path = self.mentity.as(not null).location.file.as(not null).filename
+               return file.filename.replace(base_path, "")
+       end
+end
+
+# No tests for `mentity`
+class WarningNoTest
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No nitunit files for `{mentity.full_name}`"
+end
+
+# Cmd that finds the man file related to an `mentity`
+class CmdManFile
+       super CmdEntity
+
+       # Man file
+       var file: nullable String = null
+
+       redef fun init_command do
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+
+               var mpackage = null
+               if mentity isa MPackage then
+                       mpackage = mentity
+               else if mentity isa MGroup then
+                       mpackage = mentity.mpackage
+               else if mentity isa MModule then
+                       mpackage = mentity.mpackage
+               end
+
+               if mpackage == null then return new WarningNoManFile(mentity)
+
+               var source_file = mpackage.location.file
+               if source_file == null then return new WarningNoManFile(mentity)
+
+               var man_dir = source_file.filename / "man"
+               if not man_dir.file_exists then return new WarningNoManFile(mentity)
+
+               var man_file = null
+               for file in man_dir.files do
+                       if not file.has_prefix(mentity.name) then continue
+                       man_file = man_dir / file
+               end
+               if man_file == null then return new WarningNoManFile(mentity)
+
+               self.file = man_file
+
+               return res
+       end
+end
+
+# No man file for `mentity`
+class WarningNoManFile
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No man file for `{mentity.full_name}`"
+end
+
+class CmdManSynopsis
+       super CmdManFile
+
+       # Synopsis string extracted from man
+       var synopsis: nullable String
+
+       redef fun init_command do
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+               var file = self.file.as(not null)
+
+               var lines = file.to_path.read_lines
+               var in_synopsis = false
+               for line in lines do
+                       if in_synopsis and line.has_prefix(mentity.name) then
+                               synopsis = line
+                               break
+                       end
+                       if line != "# SYNOPSIS" then continue
+                       in_synopsis = true
+               end
+
+               if synopsis == null then return new WarningNoManSynopsis(mentity)
+
+               return res
+       end
+end
+
+# No synopsis found in the man file for `mentity`
+class WarningNoManSynopsis
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No synopsis found in the man file for `{mentity.full_name}`"
+end
+
+class CmdManOptions
+       super CmdManFile
+
+       # Options description
+       var options: nullable ArrayMap[String, String]
+
+       redef fun init_command do
+               var res = super
+               if not res isa CmdSuccess then return res
+               var mentity = self.mentity.as(not null)
+               var file = self.file.as(not null)
+
+               var options = new ArrayMap[String, String]
+
+               var lines = file.to_path.read_lines
+               var in_options = false
+               for i in [0 .. lines.length[ do
+                       var line = lines[i]
+                       if line == "# OPTIONS" then
+                               in_options = true
+                       else if in_options and line.has_prefix("### ") then
+                               var opt = line.substring(4, line.length).trim.replace("`", "")
+                               var desc = ""
+                               if i < lines.length - 1 then
+                                       desc = lines[i + 1].trim
+                               end
+                               options[opt] = desc
+                       else if line.has_prefix("# ") then
+                               in_options = false
+                       end
+               end
+
+               if options.is_empty then return new WarningNoManOptions(mentity)
+               self.options = options
+
+               return res
+       end
+end
+
+# No options description found in the man file for `mentity`
+class WarningNoManOptions
+       super CmdWarning
+
+       # MEntity provided
+       var mentity: MEntity
+
+       redef fun to_s do return "No options description found in the man file for `{mentity.full_name}`"
+end
diff --git a/src/doc/commands/tests/test_commands_main.nit b/src/doc/commands/tests/test_commands_main.nit
new file mode 100644 (file)
index 0000000..5715cc2
--- /dev/null
@@ -0,0 +1,73 @@
+# 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_commands_main is test
+
+import test_commands
+import doc::commands::commands_main
+
+class TestCommandsMain
+       super TestCommands
+       test
+
+       fun test_cmd_mains is test do
+               var cmd = new CmdMains(test_view, mentity_name = "test_prog")
+               var res = cmd.init_command
+               assert res isa CmdSuccess
+               var results = cmd.results
+               assert results != null
+               assert results.length == 1
+               assert results.first.full_name == "test_prog::test_prog"
+       end
+
+       fun test_cmd_main_compile is test do
+               var cmd = new CmdMainCompile(test_view, mentity_name = "test_prog::test_prog")
+               var res = cmd.init_command
+               assert res isa CmdSuccess
+
+               var command = cmd.command
+               assert command != null
+               assert command.has_prefix("nitc ")
+               assert command.has_suffix("test_prog.nit")
+       end
+
+       fun test_cmd_testing is test do
+               var cmd = new CmdTesting(test_view, mentity_name = "test_prog")
+               var res = cmd.init_command
+               assert res isa CmdSuccess
+
+               var command = cmd.command
+               assert command != null
+               assert command.has_prefix("nitunit ")
+               assert command.has_suffix("/tests")
+       end
+
+       fun test_cmd_man_synopsis is test do
+               var cmd = new CmdManSynopsis(test_view, mentity_name = "test_prog")
+               var res = cmd.init_command
+               assert res isa CmdSuccess
+               assert cmd.synopsis == "test_prog [*options*] ARGS..."
+       end
+
+       fun test_cmd_man_options is test do
+               var cmd = new CmdManOptions(test_view, mentity_name = "test_prog")
+               var res = cmd.init_command
+               assert res isa CmdSuccess
+
+               var options = cmd.options
+               assert options != null
+               assert options["--opt1"] == "Option 1."
+               assert options["--opt2"] == "Option 2."
+       end
+end