class EIFFEL_CLASS
Class to render github like markdown found in the description note field of Eiffel classes.
note
description: "[
Class to render github like markdown found in the description note field of Eiffel classes.
]"
author: "Finnian Reilly"
copyright: "Copyright (c) 2001-2022 Finnian Reilly"
contact: "finnian at eiffel hyphen loop dot com"
license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
date: "2025-11-17 8:46:03 GMT (Monday 17th November 2025)"
revision: "70"
class
EIFFEL_CLASS
inherit
EIFFEL_CLASS_SERIALIZEABLE
undefine
is_equal
redefine
copy, make_default
end
EL_FILE_SYNC_ITEM
rename
make as make_sync_item,
source_path as html_source_path
undefine
copy, is_equal
redefine
is_modified, sink_content
end
COMPARABLE
undefine
copy
end
create
make
feature {NONE} -- Initialization
make (a_source_path: like source_path; a_library_ecf: like library_ecf; a_config: like config)
--
local
class_encoding: NATURAL; analyzer: EIFFEL_SOURCE_ANALYZER
do
relative_source_path := a_source_path.relative_path (a_config.root_dir)
source_parent_base := a_source_path.parent.base
make_from_template_and_output (
a_config.templates.eiffel_source,
a_config.output_dir + relative_source_path.with_new_extension (Html)
)
library_ecf := a_library_ecf; config := a_config; source_path := a_source_path
name := source_path.base_name.as_upper
if attached File.plain_text (source_path) as source_text then
class_encoding := source_encoding (source_text)
code_text := new_code_text (source_text, class_encoding)
create analyzer.make (source_text, class_encoding)
metrics := analyzer.metrics
end
set_class_use_set
make_sync_item (
config.output_dir, config.ftp_host, html_output_path.relative_path (config.output_dir), 0
)
create notes.make (relative_source_path.parent, config.note_fields)
ensure
initialized: name /= Empty_string and code_text /= Empty_string
end
make_default
do
create source_path
name := Empty_string
code_text := Empty_string
metrics := Default_metrics
notes := Default_notes
Precursor
end
feature -- Access
alias_name: detachable ZSTRING
-- alias for `name' when defined in ECF. `Void' if not.
class_text: ZSTRING
do
Result := XML.escaped (code_text.substring_end (class_begin_index + 1))
end
class_use_set: EL_HASH_SET [IMMUTABLE_STRING_8]
-- set of class names used in `code_text' after first feature marker
code_text: ZSTRING
metrics: SPECIAL [INTEGER]
-- metrics array in alphabetical order of name defined in class `EIFFEL_SOURCE_ANALYZER'
name: ZSTRING
notes: EIFFEL_NOTES
notes_text: ZSTRING
do
Result := XML.escaped (code_text.substring (1, class_begin_index))
end
feature -- File paths
ecf_relative_html_path: FILE_PATH
-- html path relative to `library_ecf.ecf_dir'
do
Result := source_path.relative_dot_path (library_ecf.ecf_path).with_new_extension (Html)
end
relative_html_path: FILE_PATH
-- HTML path relative to `config.root_dir'
do
Result := relative_source_path.with_new_extension (Html)
end
relative_source_path: FILE_PATH
source_path: FILE_PATH
source_parent_base: ZSTRING
-- For eg. imp_unix or imp_mswin
feature -- Status report
has_further_information: BOOLEAN
do
Result := not further_information_fields.is_empty
end
is_example: BOOLEAN
do
Result := not is_library
end
is_library: BOOLEAN
do
end
is_modified: BOOLEAN
do
if not (html_output_path.exists and digest_path.exists) then
Result := True
else
Result := previous_digest /= current_digest
end
end
is_source_modified: BOOLEAN
-- `True' if file was modified or moved since creation of `Current'
do
if attached crc_generator as crc and then attached File.plain_text (source_path) as source_text then
crc.add_string (new_code_text (source_text, source_encoding (source_text)))
crc.add_string (relative_source_path)
if initial_current_digest.to_boolean then
Result := initial_current_digest /= crc.checksum
else
Result := current_digest /= crc.checksum
end
end
end
feature -- Basic operations
check_class_references
do
fill_notes
notes.check_class_references (source_path.base)
end
fill_notes
do
notes.fill (source_path)
notes_filled := True
end
sink_source_substitutions
-- sink the values of ${<type-name>} occurrences `code_text'. Eg. ${CLASS_NAME}
do
if attached Once_crc_generator as crc then
if initial_current_digest.to_boolean then
current_digest := initial_current_digest
else
initial_current_digest := current_digest
end
crc.set_checksum (current_digest)
Class_link_list.add_to_checksum (crc, code_text)
current_digest := crc.checksum
end
end
feature -- Comparison
is_less alias "<" (other: like Current): BOOLEAN
-- Is current object less than `other'?
-- Called from {EIFFEL_CLASS_TABLE}.example_class_list
-- Ensures consistent `current_digest' during call to `{LIBRARY_CLASS}.sink_source_substitutions'
do
if name ~ other.name then
if source_parent_base ~ other.source_parent_base then
Result := relative_source_path < other.relative_source_path
else
-- For example: imp_unix VS imp_mswin
Result := source_parent_base < other.source_parent_base
end
else
Result := name < other.name
end
end
feature -- Element change
set_alias_name (a_name: ZSTRING)
do
alias_name := a_name
end
set_client_examples (class_list: ITERABLE [EIFFEL_CLASS])
do
do_nothing -- for example classes
end
feature {NONE} -- Implementation
class_begin_index: INTEGER
do
across Class_begin_strings as string until Result > 0 loop
Result := code_text.substring_index (string.item, 1)
end
end
source_encoding (source_text: STRING): NATURAL
local
utf: EL_UTF_CONVERTER
do
if utf.is_utf_8_file (source_text) then
Result := Utf_8
else
Result := Latin_1
end
end
copy (other: like Current)
do
standard_copy (other)
notes := other.notes.twin
end
further_information: ZSTRING
-- other information besides the description
local
pos_comma: INTEGER
do
Result := further_information_fields.joined_with_string (", ")
if not Result.is_empty then
pos_comma := Result.last_index_of (',', Result.count)
if pos_comma > 0 then
Result.replace_substring_general (" and", pos_comma, pos_comma)
end
Result.to_lower
end
end
further_information_fields: EL_ZSTRING_LIST
do
Result := notes.other_field_titles
end
new_code_text (raw_source: STRING; a_encoding: NATURAL): ZSTRING
local
utf: EL_UTF_CONVERTER
do
if a_encoding = Utf_8 then
create Result.make_from_utf_8 (utf.bomless_utf_8 (raw_source))
else
create Result.make (raw_source.count)
if Result.is_shareable_8 (raw_source) then
Result.share_8 (raw_source)
else
Result.append_string_general (raw_source)
end
end
end
relative_ecf_html_path: ZSTRING
do
Result := library_ecf.html_index_path.relative_dot_path (relative_source_path)
end
set_class_use_set
local
analyzer: CLASS_NAME_SET_COMPILER
do
create analyzer.make_from_zstring (code_text)
class_use_set := analyzer.class_name_set
end
sink_content (crc: like crc_generator)
do
crc.add_string (code_text)
crc.add_string (relative_source_path)
end
feature {NONE} -- Internal attributes
config: PUBLISHER_CONFIGURATION
initial_current_digest: NATURAL
-- `current_digest' before modification by `sink_source_substitutions'
library_ecf: EIFFEL_CONFIGURATION_FILE
feature {NONE} -- Evolicity fields
get_top_dir: ZSTRING
do
Result := Directory.relative_parent (relative_source_path.step_count - 1)
end
getter_function_table: like getter_functions
--
do
create Result.make_assignments (<<
["description_elements", agent: LIST [HTML_TEXT_ELEMENT] do Result := notes.description_elements end],
["note_fields", agent: like notes.field_list do Result := notes.field_list end],
["has_description", agent: BOOLEAN_REF do Result := notes.has_description.to_reference end],
["has_further_information", agent: BOOLEAN_REF do Result := has_further_information.to_reference end],
["has_fields", agent: BOOLEAN_REF do Result := notes.has_fields.to_reference end],
["is_library", agent: BOOLEAN_REF do Result := is_library.to_reference end],
["notes_text", agent notes_text],
["class_text", agent class_text],
["further_information", agent further_information],
["ecf_contents_path", agent relative_ecf_html_path],
["name", agent: STRING do Result := name.string end],
["name_as_lower", agent: STRING do Result := name.string.as_lower end],
["html_path", agent: ZSTRING do Result := ecf_relative_html_path end],
["favicon_markup_path", agent: ZSTRING do Result := config.templates.favicon_markup_path end],
["top_dir", agent get_top_dir],
["relative_dir", agent: DIR_PATH do Result := relative_source_path.parent end],
["source_path", agent: FILE_PATH do Result := relative_source_path end]
>>)
end
end