class SOURCE_MODEL

(source code)

Description

Splits Eiffel source lines into feature groups and individual feature lines

note
	description: "Splits Eiffel source lines into feature groups and individual feature lines"

	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: "2023-03-11 9:28:53 GMT (Saturday 11th March 2023)"
	revision: "16"

class
	SOURCE_MODEL

inherit
	EL_EIFFEL_SOURCE_LINE_STATE_MACHINE
		rename
			make as make_machine
		end

	EL_MODULE_FILE; EL_MODULE_LIO

	FEATURE_CONSTANTS

feature {NONE} -- Initialization

	make (a_source_path: FILE_PATH)
		local
			source_text: ZSTRING; utf: EL_UTF_CONVERTER
			space_editor: CLASS_LEADING_SPACE_EDITOR
		do
			make_machine
			source_path := a_source_path
			is_test_set := a_source_path.base.ends_with (Test_set_ending)
			create class_notes.make (10)
			create class_header.make (20)
			create class_footer.make (1)
			create group_header.make (3)
			create feature_group_list.make (8)

			create encoding.make_default

			if attached File.plain_text (a_source_path) as source_8 then
				if utf.is_utf_8_file (source_8) then
					encoding.set_utf (8)
				else
					encoding.set_latin (1)
				end
--				clean up any leading spaces at beginning of lines
				create space_editor.make_empty
				space_editor.set_source_text (source_8)
				if space_editor.leading_space_count > 0 then
					space_editor.replace_spaces
				end
				create source_text.make_from_file (source_8)
--				make sure there is no new-line after final end keyword
				source_text.right_adjust

				do_with_iterable_lines (agent find_class_declaration, source_text.split_list ('%N'))
			end
		end

feature -- Status query

	is_test_set: BOOLEAN

feature {NONE} -- State handlers

	fill_class_footer (line: ZSTRING)
		do
			class_footer.extend (line)
		end

	find_class_declaration (line: ZSTRING)
		do
			if code_line_is_class_declaration then
				state := agent find_first_feature_block
				find_first_feature_block (line)
			else
				class_notes.extend (line)
			end
		end

	find_first_feature (line: ZSTRING)
		-- find first feature in feature group
		do
			if code_line_is_feature_declaration then
				feature_group_list.extend (create {FEATURE_GROUP}.make (group_header))
				group_header.wipe_out

				feature_group_list.add_feature (line, is_test_set)
				state := agent find_next_feature
			else
				group_header.extend (line)
			end
			if code_line_is_verbatim_string_start then
				state := agent find_verbatim_string_end
			end
		end

	find_first_feature_block (line: ZSTRING)
		do
			if code_line_starts_with (0, Keyword.feature_) then
				group_header.extend (line)
				state := agent find_first_feature
			else
				class_header.extend (line)
			end
		end

	find_next_feature (line: ZSTRING)
			-- find next feature in feature group
		do
			if code_line_starts_with_one_of (0, Footer_start_keywords) then
				fill_class_footer (line)
				state := agent fill_class_footer

			elseif code_line_starts_with (0, Keyword.feature_) then
				group_header.extend (line)
				state := agent find_first_feature

			elseif code_line_is_feature_declaration then
				feature_group_list.add_feature (line, is_test_set)
				state := agent find_next_feature
			else
				feature_group_list.last.append (line)
			end
			if code_line_is_verbatim_string_start then
				state := agent find_verbatim_string_end
			end
		end

	find_verbatim_string_end (line: ZSTRING)
		do
			feature_group_list.last.append (line)
			if code_line_is_verbatim_string_end then
				state := agent find_next_feature
			end
		end

feature {CLASS_FEATURE} -- Implementation attributes

	class_footer: SOURCE_LINES

	class_header: SOURCE_LINES

	class_notes: SOURCE_LINES

	encoding: EL_ENCODING

	feature_group_list: FEATURE_GROUP_LIST

	group_header: EL_ZSTRING_LIST

	source_path: FILE_PATH

feature {NONE} -- Constants

	Test_set_ending: ZSTRING
		once
			Result := "_test_set.e"
		end

end