<commit_before># This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2008 <NAME> <<EMAIL>>
#
# 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.

# The main module of the nitdoc program
package nitdoc

import syntax
private import utils
import abstracttool


# Store knowledge and facilities to generate files
class DocContext
	super AbstractCompiler
	# Destination directory
	readable writable var _dir: String = "doc"

	# Content of a generated file
	var _stage_context: StageContext = new StageContext(null)

	# Add a string in the content
	fun add(s: String) do
		_stage_context.content.add(s)
		_stage_context.validate = true
	end

	# Add a string in the content iff some other string are added
	fun stage(s: String) do _stage_context.content.add(s)

	# Create a new stage in the content
	fun open_stage do _stage_context = new StageContext(_stage_context)

	# Close the current stage in the content
	fun close_stage
	do
		var s = _stage_context.parent
		if _stage_context.validate then
			s.content.add_all(_stage_context.content)
			s.validate = true
		end
		assert s != null
		_stage_context = s
	end

	# Write the content to a new file
	fun write_to(filename: String)
	do
		var f = new OFStream.open(filename)
		for s in _stage_context.content do
			f.write(s)
		end
		f.close
	end

	# Start a new file
	fun clear
	do
		_stage_context = new StageContext(null)
	end

	# Sorter of entities in alphabetical order
	var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]

	# Sort entities in the alphabetical order
	fun sort(array: Array[MMEntity])
	do
		_sorter.sort(array)
	end

	readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
	readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
	readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public")
	readable var _opt_private: OptionBool = new OptionBool("Generate the private API", "--private")
	readable var _opt_nodot: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot")
	readable var _opt_sharedir: OptionString = new OptionString("Directory containing the nitdoc files", "--sharedir")
	readable var _opt_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page", "--overview-text")
	readable var _opt_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--footer-text")
	var sharedir: nullable String

	fun public_only: Bool
	do
		if self._opt_public.value == true then return true
		return false
	end

	fun with_private: Bool
	do
		if self._opt_private.value == true then return true
		return false
	end

	# The current processed filename
	var filename: String

	# The main virtual module
	var mainmod: nullable MMVirtualModule

	redef fun perform_work(mods)
	do
		mainmod = new MMVirtualModule(self, mods)

		dir.mkdir

		sys.system("cp -r '{sharedir.to_s}'/* {dir}/")

		# Compute the set of direct owned nested modules
		var owns = new HashMap[MMModule, Array[MMModule]]
		for mod in modules do
			owns[mod] = new Array[MMModule]# [mod]
		end
		for mod in modules do
			if mod == mainmod then continue
			var d = mod.directory
			loop
				var o = d.owner
				if o != null and o != mod then
					owns[o].add(mod)
				end
				var dp = d.parent
				if dp == null or dp == d then break
				d = dp
			end
		end

		# Builds the various module hierarchies
		var mnh = new PartialOrder[MMModule] # nested module hierarchy
		var tmh = new PartialOrder[MMModule] # top module import hierrchy
		var ms = mainmod.mhe.linear_extension.reversed
		for m in ms do
			if ms == mainmod then continue
			m.mnhe_ = mnh.add(m, owns[m])
			var pub = new Array[MMModule]
			for m2 in m.mhe.greaters do
				if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue
				if m.mnhe <= m2 then continue
				if m.visibility_for(m2) <= 0 then
					# nothing
				else if m.visibility_for(m2) == 1 then
				else
					pub.add(m2)
				end
			end
			m.tmhe_ = tmh.add(m, pub)
		end

		var head = "<meta charset=\"utf-8\">" +
			"<script type=\"text/javascript\" src=\"scripts/jquery-1.7.1.min.js\"></script>\n" + 
			"<script type=\"text/javascript\" src=\"scripts/js-facilities.js\"></script>\n" +
			"<link rel=\"stylesheet\" href=\"styles/main.css\" type=\"text/css\"  media=\"screen\" />"

		var action_bar = "<header><nav class='main'><ul><li class=\"current\">Overview</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"

		var overview_text = ""
		if self._opt_overview_text.value != null then overview_text = self._opt_overview_text.value.as(not null)

		var footer_text = ""
		if self._opt_footer_text.value != null then footer_text = self._opt_footer_text.value.as(not null)

		# generate the index
		self.filename = "index.html"
		clear
		add("<!DOCTYPE html>")
		add("<html><head>{head}<title>Index</title></head><body>\n")
		add(action_bar)
		add("<div class=\"page\">")
		add("<div class=\"content fullpage\">")
		add("<h1>Modules</h1>\n<article class='overview'>{overview_text}<ul>")
		var modss = mainmod.mhe.greaters_and_self.to_a
		sort(modss)
		for mod in modss do
			if not mod.is_toplevel then continue
			if not mod.require_doc(self) then continue
			assert mod isa MMSrcModule
			add("<li>{mod.html_link(self)} {mod.short_doc}</li>")

		end
		add("</ul>")

		var op = new Buffer
		op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
		for mod in modss do
			if not mod.is_toplevel then continue
			if not mod.require_doc(self) then continue
			op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n")
			for mod2 in mod.tmhe.direct_greaters do
				if not modss.has(mod2) then continue
				op.append("\"{mod.name}\"->\"{mod2.name}\";\n")
			end
		end
		op.append("\}\n")
		self.gen_dot(op.to_s, "dep", "Modules hierarchy")
		add("</article></div>")
		add("<div class='clear'></div>")
		add("</div>")
		add("<footer>{footer_text}</footer>")
		add("</body></html>\n")
		write_to("{dir}/index.html")

		# Generate page for modules
		for mod in modules do
			if mod == mainmod then continue
			assert mod isa MMSrcModule
			if not mod.require_doc(self) then continue
			self.filename = mod.html_name
			action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li class=\"current\">{mod.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
			clear
			add("<!DOCTYPE html>")
			add("<html><head>{head}<title>Module {mod.name}</title></head><body>\n")
			add(action_bar)
			add("<div class=\"page\">")
			mod.file_page_doc(self)
			add("</div>")
			add("<footer>{footer_text}</footer>")
			add("</body></html>\n")
			write_to("{dir}/{mod.html_name}.html")
		end

		# Generate pages for global classes
		for c in mainmod.local_classes do
			if not c.require_doc(self) then continue
			self.filename = c.html_name
			action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li>{c.global.intro.mmmodule.toplevel_owner.html_link(self)}</li><li class=\"current\">{c.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
			clear
			add("<!DOCTYPE html>")
			add("<html><head>{head}<title>Class {c.name}</title></head><body>\n")
			add(action_bar)
			add("<div class=\"page\">")
			c.file_page_doc(self)
			add("</div>")
			add("<footer>{footer_text}</footer>")
			add("</body></html>\n")
			write_to("{dir}/{c.html_name}.html")
		end

		self.filename = "fullindex"
		action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li class=\"current\">Full Index</li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
		clear
		add("<!DOCTYPE html>")
		add("<html><head>{head}<title>Full Index</title></head><body>\n")
		add(action_bar)
		add("<div class=\"page\">")
		add("<div class=\"content fullpage\">")
		mainmod.file_index_page_doc(self)
		add("</div>")
		add("</div>")
		add("<footer>{footer_text}</footer>")
		add("</body></html>\n")
		write_to("{dir}/full-index.html")
	end


	# Add a (source) link fo a given location
	fun show_source(l: Location)
	do
		var s = opt_source.value
		if s == null then
			add("in #{l.file.filename.simplify_path}")
		else
			# THIS IS JUST UGLY ! (but there is no replace yet)
			var x = s.split_with("%f")
			s = x.join(l.file.filename.simplify_path)
			x = s.split_with("%l")
			s = x.join(l.line_start.to_s)
			x = s.split_with("%L")
			s = x.join(l.line_end.to_s)
			add(" (<a href=\"{s}\">show code</a>)")
		end
	end

	# Generate a clicable graphiz image using a dot content.
	# `name' refer to the filename (without extension) and the id name of the map.<commit_msg>nitdoc: useless # in show_source<commit_after># This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2008 <NAME> <<EMAIL>>
#
# 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.

# The main module of the nitdoc program
package nitdoc

import syntax
private import utils
import abstracttool


# Store knowledge and facilities to generate files
class DocContext
	super AbstractCompiler
	# Destination directory
	readable writable var _dir: String = "doc"

	# Content of a generated file
	var _stage_context: StageContext = new StageContext(null)

	# Add a string in the content
	fun add(s: String) do
		_stage_context.content.add(s)
		_stage_context.validate = true
	end

	# Add a string in the content iff some other string are added
	fun stage(s: String) do _stage_context.content.add(s)

	# Create a new stage in the content
	fun open_stage do _stage_context = new StageContext(_stage_context)

	# Close the current stage in the content
	fun close_stage
	do
		var s = _stage_context.parent
		if _stage_context.validate then
			s.content.add_all(_stage_context.content)
			s.validate = true
		end
		assert s != null
		_stage_context = s
	end

	# Write the content to a new file
	fun write_to(filename: String)
	do
		var f = new OFStream.open(filename)
		for s in _stage_context.content do
			f.write(s)
		end
		f.close
	end

	# Start a new file
	fun clear
	do
		_stage_context = new StageContext(null)
	end

	# Sorter of entities in alphabetical order
	var _sorter: AlphaSorter[MMEntity] = new AlphaSorter[MMEntity]

	# Sort entities in the alphabetical order
	fun sort(array: Array[MMEntity])
	do
		_sorter.sort(array)
	end

	readable var _opt_dir: OptionString = new OptionString("Directory where doc is generated", "-d", "--dir")
	readable var _opt_source: OptionString = new OptionString("What link for source (%f for filename, %l for first line, %L for last line)", "--source")
	readable var _opt_public: OptionBool = new OptionBool("Generate only the public API", "--public")
	readable var _opt_private: OptionBool = new OptionBool("Generate the private API", "--private")
	readable var _opt_nodot: OptionBool = new OptionBool("Do not generate graphes with graphviz", "--no-dot")
	readable var _opt_sharedir: OptionString = new OptionString("Directory containing the nitdoc files", "--sharedir")
	readable var _opt_overview_text: OptionString = new OptionString("Text displayed as introduction of Overview page", "--overview-text")
	readable var _opt_footer_text: OptionString = new OptionString("Text displayed as footer of all pages", "--footer-text")
	var sharedir: nullable String

	fun public_only: Bool
	do
		if self._opt_public.value == true then return true
		return false
	end

	fun with_private: Bool
	do
		if self._opt_private.value == true then return true
		return false
	end

	# The current processed filename
	var filename: String

	# The main virtual module
	var mainmod: nullable MMVirtualModule

	redef fun perform_work(mods)
	do
		mainmod = new MMVirtualModule(self, mods)

		dir.mkdir

		sys.system("cp -r '{sharedir.to_s}'/* {dir}/")

		# Compute the set of direct owned nested modules
		var owns = new HashMap[MMModule, Array[MMModule]]
		for mod in modules do
			owns[mod] = new Array[MMModule]# [mod]
		end
		for mod in modules do
			if mod == mainmod then continue
			var d = mod.directory
			loop
				var o = d.owner
				if o != null and o != mod then
					owns[o].add(mod)
				end
				var dp = d.parent
				if dp == null or dp == d then break
				d = dp
			end
		end

		# Builds the various module hierarchies
		var mnh = new PartialOrder[MMModule] # nested module hierarchy
		var tmh = new PartialOrder[MMModule] # top module import hierrchy
		var ms = mainmod.mhe.linear_extension.reversed
		for m in ms do
			if ms == mainmod then continue
			m.mnhe_ = mnh.add(m, owns[m])
			var pub = new Array[MMModule]
			for m2 in m.mhe.greaters do
				if m2.toplevel_owner != m2 and m2.toplevel_owner != m.toplevel_owner then continue
				if m.mnhe <= m2 then continue
				if m.visibility_for(m2) <= 0 then
					# nothing
				else if m.visibility_for(m2) == 1 then
				else
					pub.add(m2)
				end
			end
			m.tmhe_ = tmh.add(m, pub)
		end

		var head = "<meta charset=\"utf-8\">" +
			"<script type=\"text/javascript\" src=\"scripts/jquery-1.7.1.min.js\"></script>\n" + 
			"<script type=\"text/javascript\" src=\"scripts/js-facilities.js\"></script>\n" +
			"<link rel=\"stylesheet\" href=\"styles/main.css\" type=\"text/css\"  media=\"screen\" />"

		var action_bar = "<header><nav class='main'><ul><li class=\"current\">Overview</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"

		var overview_text = ""
		if self._opt_overview_text.value != null then overview_text = self._opt_overview_text.value.as(not null)

		var footer_text = ""
		if self._opt_footer_text.value != null then footer_text = self._opt_footer_text.value.as(not null)

		# generate the index
		self.filename = "index.html"
		clear
		add("<!DOCTYPE html>")
		add("<html><head>{head}<title>Index</title></head><body>\n")
		add(action_bar)
		add("<div class=\"page\">")
		add("<div class=\"content fullpage\">")
		add("<h1>Modules</h1>\n<article class='overview'>{overview_text}<ul>")
		var modss = mainmod.mhe.greaters_and_self.to_a
		sort(modss)
		for mod in modss do
			if not mod.is_toplevel then continue
			if not mod.require_doc(self) then continue
			assert mod isa MMSrcModule
			add("<li>{mod.html_link(self)} {mod.short_doc}</li>")

		end
		add("</ul>")

		var op = new Buffer
		op.append("digraph dep \{ rankdir=BT; node[shape=none,margin=0,width=0,height=0,fontsize=10]; edge[dir=none,color=gray]; ranksep=0.2; nodesep=0.1;\n")
		for mod in modss do
			if not mod.is_toplevel then continue
			if not mod.require_doc(self) then continue
			op.append("\"{mod.name}\"[URL=\"{mod.html_name}.html\"];\n")
			for mod2 in mod.tmhe.direct_greaters do
				if not modss.has(mod2) then continue
				op.append("\"{mod.name}\"->\"{mod2.name}\";\n")
			end
		end
		op.append("\}\n")
		self.gen_dot(op.to_s, "dep", "Modules hierarchy")
		add("</article></div>")
		add("<div class='clear'></div>")
		add("</div>")
		add("<footer>{footer_text}</footer>")
		add("</body></html>\n")
		write_to("{dir}/index.html")

		# Generate page for modules
		for mod in modules do
			if mod == mainmod then continue
			assert mod isa MMSrcModule
			if not mod.require_doc(self) then continue
			self.filename = mod.html_name
			action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li class=\"current\">{mod.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
			clear
			add("<!DOCTYPE html>")
			add("<html><head>{head}<title>Module {mod.name}</title></head><body>\n")
			add(action_bar)
			add("<div class=\"page\">")
			mod.file_page_doc(self)
			add("</div>")
			add("<footer>{footer_text}</footer>")
			add("</body></html>\n")
			write_to("{dir}/{mod.html_name}.html")
		end

		# Generate pages for global classes
		for c in mainmod.local_classes do
			if not c.require_doc(self) then continue
			self.filename = c.html_name
			action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li>{c.global.intro.mmmodule.toplevel_owner.html_link(self)}</li><li class=\"current\">{c.name}</li><li><a href='full-index.html'>Full Index</a></li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
			clear
			add("<!DOCTYPE html>")
			add("<html><head>{head}<title>Class {c.name}</title></head><body>\n")
			add(action_bar)
			add("<div class=\"page\">")
			c.file_page_doc(self)
			add("</div>")
			add("<footer>{footer_text}</footer>")
			add("</body></html>\n")
			write_to("{dir}/{c.html_name}.html")
		end

		self.filename = "fullindex"
		action_bar = "<header><nav class='main'><ul><li><a href='./index.html'>Overview</a></li><li class=\"current\">Full Index</li><li><a href=\"help.html\">Help</a></li></ul></nav></header>\n"
		clear
		add("<!DOCTYPE html>")
		add("<html><head>{head}<title>Full Index</title></head><body>\n")
		add(action_bar)
		add("<div class=\"page\">")
		add("<div class=\"content fullpage\">")
		mainmod.file_index_page_doc(self)
		add("</div>")
		add("</div>")
		add("<footer>{footer_text}</footer>")
		add("</body></html>\n")
		write_to("{dir}/full-index.html")
	end


	# Add a (source) link fo a given location
	fun show_source(l: Location)
	do
		var s = opt_source.value
		if s == null then
			add("in {l.file.filename.simplify_path}")
		else
			# THIS IS JUST UGLY ! (but there is no replace yet)
			var x = s.split_with("%f")
			s = x.join(l.file.filename.simplify_path)
			x = s.split_with("%l")
			s = x.join(l.line_start.to_s)
			x = s.split_with("%L")
			s = x.join(l.line_end.to_s)
			add(" (<a href=\"{s}\">show code</a>)")
		end
	end

	# Generate a clicable graphiz image using a dot content.
	# `name' refer to the filename (without extension) and the id name of the map.
{'fraction_non_alphanumeric': 0.15342105263157896, 'fraction_numerical': 0.003789473684210526, 'mean_word_length': 3.998947645356485, 'pattern_counts': {'":': 0, '<': 309, '<?xml version=': 0, '>': 307, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4067874', 'n_tokens_mistral': 7076, 'n_tokens_neox': 6407, 'n_words': 1778}