Very often you need to break plaintext at a specific width while retaining readability. However, it is easy to get caught up in fencepost errors and similar irritating issues. I wrote the particular implementation here to help display the cheat files on Cheat. I have no doubt that this has been done many times before, but my method does do a few neat things.
features
Indents are preserved.
Linebreaks never occur within words unless the word is longer than the maximum width.
You can set an additional hanging indent so that wrapped lines clearly belong to their parent.
Also optionally, you can have it hang wrapped lines in a list underneath their parent text instead of underneath the list item delimiter.
usage examples
class Employeed < Status
belongs_to :employee, :class_name => "Person", :foreign_key => 'employee_id'
end
1. To find out what class an instance of the superclass use @instance.class or you can use the value of the type column from other systems that do not go through AR.
2. Fixtures for a STI model must go in the fixture file for the superclass.
Straightforward wrap (notice how the belongs_to
line preserves its indent):
chloe:~/Projects/misc/misc eweaver$ ./wrap.rb text 50 0 false
class Employeed < Status
belongs_to :employee, :class_name => "Person",
:foreign_key => 'employee_id'
end
1. To find out what class an instance of the
superclass use @instance.class or you can use the
value of the type column from other systems that
do not go through AR.
2. Fixtures for a STI model must go in the fixture
file for the superclass.
How about with some hanging indents on the list, too?
chloe:~/Projects/misc/misc eweaver$ ./wrap.rb text 50 0 true
class Employeed < Status
belongs_to :employee, :class_name => "Person",
:foreign_key => 'employee_id'
end
1. To find out what class an instance of the
superclass use @instance.class or you can use
the value of the type column from other systems
that do not go through AR.
2. Fixtures for a STI model must go in the fixture
file for the superclass.
Etc.
code
#!/opt/local/bin/ruby
class String
def wrap(width, hanging_indent = 0, magic_lists = false)
lines = self.split(/\n/)
lines.collect! do |line|
if magic_lists
line =~ /^([\s\-\d\.\:]*\s)/
else
line =~ /^([\s]*\s)/
end
indent = $1.length + hanging_indent rescue hanging_indent
buffer = ""
first = true
while line.length > 0
first ? (i, first = 0, false) : i = indent
pos = width - i
if line.length > pos and line[0..pos] =~ /^(.+)\s/
subline = $1
else
subline = line[0..pos]
end
buffer += " " * i + subline + "\n"
line.tail!(subline.length)
end
buffer[0..-2]
end
lines.join("\n")
end
def tail!(pos)
self[0..pos] = ""
strip!
end
end
if __FILE__ == $0
File.open(ARGV[0]) do |f|
puts f.read.wrap(ARGV[1].to_i, ARGV[2].to_i, ARGV[3] == "true")
end
end
It’s really just a few regular expressions and some string munging; that’s all.
That’s some good stuff. Very good on the hanging indent stuff! I will definitely use it for the cheat libraries I am writing
Cha ching! Money.
I think there is small bug in the code.. Try having a line with a single word of more than wrap characters: in this case, the last character of the first line gets truncated.
This can be fixed by having
line.tail!(subline.length)
changed to
line.tail!(subline.length-1)