class MARKDOWN_TRANSLATER
Translates class note markdown to Github markdown
note
description: "Translates class note markdown to Github markdown"
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: "2024-03-12 16:05:23 GMT (Tuesday 12th March 2024)"
revision: "36"
class
MARKDOWN_TRANSLATER
inherit
EL_PLAIN_TEXT_LINE_STATE_MACHINE
rename
make as make_machine
end
EL_MODULE_TUPLE
MARKDOWN_ROUTINES
PUBLISHER_CONSTANTS
rename
new_line as new_line_character
end
EL_ZSTRING_CONSTANTS; EL_SHARED_ZSTRING_BUFFER_SCOPES
create
make
feature {NONE} -- Initialization
make (a_repository: REPOSITORY_PUBLISHER)
do
repository := a_repository
website_root := a_repository.web_address + char ('/')
create line_type_list.make (50)
create variable_substitution.make (repository.github_url)
state_add_code_lines := agent add_code_lines
make_machine
end
feature -- Basic operations
to_github_markdown (class_note_markdown_lines: EL_ZSTRING_LIST): ZSTRING
-- Github markdown string translated from `class_note_markdown_lines'
local
line_list: EL_ZSTRING_LIST; type: NATURAL_8; buffer: ZSTRING
do
do_with_lines (agent add_normal_text, normalized_paragraphs (class_note_markdown_lines))
if state = state_add_code_lines then
close_code_block (Empty_string.twin)
end
create line_list.make (line_type_list.count * 2)
across String_scope as scope loop
buffer := scope.best_item (500)
if attached line_type_list as list then
from list.start until list.after loop
type := list.item_key
buffer.wipe_out; buffer.append (list.item_value)
inspect type
when Empty_line, Code_marker then
line_list.extend (list.item_value)
when Code_line then
if buffer.starts_with_character ('%T') then
buffer.remove_head (1)
end
buffer.replace_substring_all (tab, space * 3)
remove_class_link_markers (buffer)
line_list.extend (buffer.twin)
when List_item, Normal_line then
translate (buffer)
line_list.extend (buffer.twin)
else
end
list.forth
end
end
end
line_list.extend (Empty_string)
Result := line_list.joined_lines
line_type_list.wipe_out
ensure
empty_line_type_list: line_type_list.is_empty
end
feature {NONE} -- Line states
add_normal_text (line: ZSTRING)
do
if line.starts_with_character ('%T') then
line_type_list.extend (Code_marker, Eiffel_code_marker)
state := state_add_code_lines
add_code_lines (line)
elseif line.count = 0 then
line_type_list.extend (Empty_line, line)
elseif is_list_item (line) then
line_type_list.extend (List_item, line)
else
line_type_list.extend (Normal_line, line)
end
end
add_code_lines (line: ZSTRING)
do
if line.is_empty or else line.starts_with_character ('%T') then
line_type_list.extend (Code_line, line)
else
close_code_block (line)
end
end
close_code_block (line: ZSTRING)
do
line_type_list.extend (Code_marker, char ('`') * 4)
state := agent add_normal_text
add_normal_text (line)
end
feature {NONE} -- Implementation
as_pecf_path (link_path: FILE_PATH): FILE_PATH
-- Eg. "library/base/base.reflection.html" -> "library/base/base.pecf"
do
Result := link_path.twin
Result.remove_extension
if Result.has_dot_extension then
Result.remove_extension
end
Result.add_extension (Extension.pecf)
end
normalized_paragraphs (markdown_lines: EL_ZSTRING_LIST): EL_ZSTRING_LIST
-- join consecutive "normal lines" that are not bullet point or numbered items
local
line: ZSTRING; previous_type, type: NATURAL_8; i: INTEGER
do
create Result.make_from_array (markdown_lines.to_array)
from Result.start until Result.after loop
line := Result.item
if line.count > 0 and then not (line.starts_with_character ('%T') or is_list_item (line)) then
type := Normal_line
else
type := 0
end
if type = Normal_line and previous_type = Normal_line then
-- join with previous line to make a paragraph
i := Result.index - 1
Result [i] := space.joined (Result [i], line)
Result.remove
else
Result.forth
end
previous_type := type
end
end
translate (text: ZSTRING)
do
-- change [http://address.com click here] to [click here](http://address.com)
-- in order to be compatible with Github markdown
across Link_types as type loop
text.edit (type.item, char (']'), agent to_github_link)
end
variable_substitution.substitute_links (text)
replace_apostrophes (text); text.replace_substring_all ("''", "*")
end
remove_class_link_markers (line: ZSTRING)
-- Remove any class links because they won't work in Github markdown
do
if line.starts_with_character ('%T') then
line.remove_head (1)
end
if line.has_substring (Dollor_left_brace) and then attached Class_link_list as list then
list.parse (line)
-- iterate in reverse to allow removals
from list.finish until list.before loop
line.remove (list.item.end_index) -- '}'
line.remove_substring (list.item.start_index, list.item.start_index + 1) -- "${"
list.back
end
end
end
replace_apostrophes (text: ZSTRING)
-- change (`xx') to (`xx`) in order be compatible with Github markdown
local
pos_grave, pos_apostrophe: INTEGER; done: BOOLEAN
do
from done := False until done or pos_grave > text.count loop
pos_grave := text.index_of ('`', pos_grave + 1)
if pos_grave > 0 then
pos_apostrophe := text.index_of ('%'', pos_grave + 1)
if pos_apostrophe > 0 then
text.put ('`', pos_apostrophe)
pos_grave := pos_apostrophe
end
else
done := True
end
end
end
to_github_link (start_index, end_index: INTEGER; substring: ZSTRING)
local
link_text: ZSTRING; space_index: INTEGER; link_path: FILE_PATH; link_uri: EL_FILE_URI_PATH
do
substring.to_canonically_spaced
space_index := substring.index_of (' ', 1)
if space_index > 0 then
if substring.count > 4 and then substring.same_characters (Current_dir_forward_slash, 1, 2, 2) then
link_path := substring.substring (4, space_index - 1)
if link_path.has_extension (Extension.html) and then attached as_pecf_path (link_path) as pecf_path
and then (repository.root_dir + pecf_path).exists
then
-- possibility here to add a line number for github like: base/base.pecf#L75
link_uri := repository.github_url + pecf_path
elseif not (repository.root_dir + link_path).exists then
-- Eg. http://www.eiffel-loop.com/benchmark/ZSTRING-benchmarks-latin-1.html
link_uri := website_root + link_path
else
link_uri := repository.github_url + link_path
end
else
link_uri := substring.substring (2, space_index - 1)
end
link_text := substring.substring (space_index + 1, substring.count - 1)
substring.wipe_out
substring.append (Github_link_template #$ [link_text, link_uri])
else
substring.remove_head (1)
substring.remove_tail (1)
end
end
feature {NONE} -- Internal attributes
line_type_list: EL_ARRAYED_MAP_LIST [NATURAL_8, ZSTRING]
state_add_code_lines: PROCEDURE [ZSTRING]
repository: REPOSITORY_PUBLISHER
last_is_line_item: BOOLEAN
variable_substitution: GITHUB_TYPE_VARIABLE_SUBSTITUTION
website_root: EL_DIR_URI_PATH
-- Eg. http://www.eiffel-loop.com/
feature {NONE} -- Line types
Code_line: NATURAL_8 = 1
Code_marker: NATURAL_8 = 2
Empty_line: NATURAL_8 = 3
List_item: NATURAL_8 = 4
Normal_line: NATURAL_8 = 5
feature {NONE} -- Constants
Extension: TUPLE [html, pecf: ZSTRING]
once
create Result
Tuple.fill (Result, "html, pecf")
end
Link_types: EL_ZSTRING_LIST
once
Result := "[http://, [https://, [./"
end
Eiffel_code_marker: ZSTRING
once
Result := char ('`') * 4 + "eiffel"
end
end