note
description: "Simple formatter of Eiffel code text. Converts Eiffel code text into%
%corresponding XML tagged eiffel code text."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
CODE_FORMATTER
inherit
CODE_FORMATTER_CONSTANTS
feature -- Access
text: STRING
-- The formatted text
last_formatted_line: STRING
-- Last line which underwent formatting
feature -- Commands
format (a_text: STRING)
-- Format `a_text'. Result put into `text'
require
text_not_void: a_text /= Void
text_not_empty: not a_text.is_empty
local
l_index,
l_cnt: INTEGER
l_line: STRING
l_char: CHARACTER
do
prepare_for_formatting (a_text)
create text.make_empty
create l_line.make_empty
from
l_index := 1
l_cnt := a_text.count
until
l_index > l_cnt
loop
l_char := a_text.item (l_index)
l_line.append_character (l_char)
if l_char.out.is_equal (newline_string) or l_index = l_cnt then
format_line (l_line)
text.append (last_formatted_line)
l_line.wipe_out
end
l_index := l_index + 1
end
ensure
has_text: text /= Void
end
format_line (a_line: STRING)
-- Format `a_line', put result into `last_formatted_line'
local
l_last_char: CHARACTER
l_done,
l_was_new: BOOLEAN
l_index,
l_cnt: INTEGER
l_head: STRING
do
-- Remove the head spaces and tabs
from
l_index := 1
l_cnt := a_line.count
create l_head.make_empty
until
l_index > l_cnt or l_done
loop
l_last_char := a_line.item (l_index)
if l_last_char.out.is_equal (tab_string) or l_last_char.out.is_equal (space_string) then
l_head.append_character (l_last_char)
else
l_done := True
end
l_index := l_index + 1
end
if not l_head.is_empty then
a_line.remove_head (l_head.count)
end
-- Remove the newline tail
if not a_line.is_empty then
if a_line.item (a_line.count) = '%N' then
a_line.remove_tail (1)
l_was_new := True
end
end
-- Format
if not a_line.is_empty then
if is_comment (a_line.twin) then
a_line.prepend ("")
a_line.append ("")
else
format_all_feature_names (a_line)
format_all_symbols (a_line)
format_all_keywords (a_line)
format_class_names (a_line)
end
end
last_formatted_line := a_line.twin
-- Restore head
if not l_head.is_empty then
last_formatted_line.prepend (l_head)
end
-- Restore tail
if l_was_new then
last_formatted_line.append (newline_string)
end
end
feature {NONE} -- Implementation
prepare_for_formatting (a_text: STRING)
-- Prepare `a_text' for formatting
do
from
format_tags.start
until
format_tags.after
loop
if a_text.has_substring (format_tags.item_for_iteration) then
a_text.replace_substring_all (format_tags.item_for_iteration, empty_string)
if a_text.has_substring (format_tags.key_for_iteration) then
a_text.replace_substring_all (format_tags.key_for_iteration, empty_string)
end
end
format_tags.forth
end
end
format_all_feature_names (a_line: STRING)
-- Format all recognized feature names
local
l_index,
l_start_index,
l_end_index,
l_cnt: INTEGER
l_done,
l_found: BOOLEAN
do
from
l_index := 1
until
l_done
loop
l_index := a_line.index_of (dot_char, l_index)
if l_index > 1 then
-- Determine previous word
from
l_start_index := l_index - 1
l_found := False
until
l_found or l_start_index = 0
loop
if not is_valid_feature_char (a_line.item (l_start_index)) then
-- A non-alphanumeric character implies end of feature name
l_found := True
else
l_start_index := l_start_index - 1
end
end
a_line.insert_string ("", l_start_index + 1)
a_line.insert_string ("", l_index + ("").count)
-- Take into account inserted string dimension
l_index := l_index + ("").count + 1
l_found := False
-- Determine if this dot call is only one level deep (a.b). If so, format the call, if
-- not it will be formatted by the next format iteration.
if not (l_index > a_line.count) then
from
l_start_index := l_index
l_end_index := l_start_index
l_cnt := a_line.count
until
l_found or l_end_index > l_cnt
loop
if not is_valid_feature_char (a_line.item (l_end_index)) then
if a_line.item (l_end_index) = (dot_char) then
a_line.insert_string ("", l_start_index)
a_line.insert_string ("", l_end_index + ("").count)
l_start_index := l_end_index + ("").count + 1
l_end_index := l_start_index
l_cnt := a_line.count
else
l_found := True
end
else
l_end_index := l_end_index + 1
end
end
a_line.insert_string ("", l_start_index)
a_line.insert_string ("", l_end_index + ("").count)
l_index := l_end_index
end
else
l_done := True
end
end
end
format_all_symbols (a_line: STRING)
-- Format all recognized code symbols
do
from
a_line.append (space_string)
symbols.start
until
symbols.after
loop
if a_line.has_substring (symbols.item) then
a_line.replace_substring_all (symbols.item, "" + symbols.item + "")
end
symbols.forth
end
a_line.remove_tail (1)
end
format_all_keywords (a_line: STRING)
-- Format all recognized code keywords
do
from
keywords.start
a_line.append (space_string)
until
keywords.after
loop
if a_line.has_substring (keywords.item + space_string) then
a_line.replace_substring_all (keywords.item + space_string, "" + keywords.item + "" + space_string)
end
if a_line.has_substring (keywords.item + newline_string) then
a_line.replace_substring_all (keywords.item + newline_string, "" + keywords.item + "" + newline_string)
end
if a_line.has_substring (keywords.item + tab_string) then
a_line.replace_substring_all (keywords.item + tab_string, "" + keywords.item + "" + tab_string)
end
keywords.forth
end
a_line.remove_tail (1)
end
format_class_names (a_line: STRING)
-- Format all recognized class names
local
l_index,
l_cnt: INTEGER
l_char: CHARACTER
l_substring: STRING
do
from
l_index := 1
create l_substring.make_empty
a_line.append (tab_string)
l_cnt := a_line.count
until
l_index > l_cnt
loop
l_char := a_line.item (l_index)
if not is_valid_feature_char (l_char) then
if is_uppercase (l_substring) and not l_substring.is_equal ("NONE") then
a_line.replace_substring ("" + l_substring + "", l_index - l_substring.count, l_index - 1)
l_index := l_index + ("").count - 1
l_cnt := a_line.count
end
l_substring.wipe_out
else
l_substring.append_character (l_char)
end
l_index := l_index + 1
end
a_line.remove_tail (1)
end
feature {NONE} -- Query
is_valid_feature_char (a_char: CHARACTER): BOOLEAN
-- Is `a_char' acceptable in a feature name (or class name)?
do
Result := a_char.is_alpha or a_char.is_digit or a_char = '_'
end
is_comment (a_line: STRING): BOOLEAN
-- Is `a_line' a comment?
do
a_line.prune_all_leading (newline_string.item (1))
a_line.prune_all_leading (space_string.item (1))
a_line.prune_all_leading (tab_string.item (1))
if a_line.count > 1 then
Result := a_line.item (1) = '-' and a_line.item (2) = '-'
end
end
is_uppercase (a_string: STRING): BOOLEAN
-- Are all alpha characters in `a_string' uppercase?
local
l_index: INTEGER
l_char: CHARACTER
do
from
l_index := 1
until
l_index > a_string.count
loop
l_char := a_string.item (l_index)
if l_char.is_alpha then
Result := l_char.is_upper
end
l_index := l_index + 1
end
end
note
copyright: "Copyright (c) 1984-2006, Eiffel Software"
license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
licensing_options: "http://www.eiffel.com/licensing"
copying: "[
This file is part of Eiffel Software's Eiffel Development Environment.
Eiffel Software's Eiffel Development Environment is free
software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published
by the Free Software Foundation, version 2 of the License
(available at the URL listed under "license" above).
Eiffel Software's Eiffel Development Environment is
distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with Eiffel Software's Eiffel Development
Environment; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
]"
source: "[
Eiffel Software
356 Storke Road, Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end -- class CODE_FORMATTER