Return right-most extension (without the dot)

Only the last extension is returned. There is no special case for combined extensions.

assert "file.txt".file_extension      == "txt"
assert "file.tar.gz".file_extension   == "gz"

For file without extension, null is returned. Hoever, for trailing dot, "" is returned.

assert "file".file_extension          == null
assert "file.".file_extension         == ""

The starting dot of hidden files is never considered.

assert ".file.txt".file_extension     == "txt"
assert ".file".file_extension         == null

Property definitions

core :: file $ Text :: file_extension
	# Return right-most extension (without the dot)
	#
	# Only the last extension is returned.
	# There is no special case for combined extensions.
	#
	#     assert "file.txt".file_extension      == "txt"
	#     assert "file.tar.gz".file_extension   == "gz"
	#
	# For file without extension, `null` is returned.
	# Hoever, for trailing dot, `""` is returned.
	#
	#     assert "file".file_extension          == null
	#     assert "file.".file_extension         == ""
	#
	# The starting dot of hidden files is never considered.
	#
	#     assert ".file.txt".file_extension     == "txt"
	#     assert ".file".file_extension         == null
	fun file_extension: nullable String
	do
		var last_slash = chars.last_index_of('.')
		if last_slash > 0 then
			return substring( last_slash+1, length ).to_s
		else
			return null
		end
	end
lib/core/file.nit:1299,2--1325,4

core :: file $ FlatString :: file_extension
	redef fun file_extension do
		var its = _items
		var p = last_byte
		var c = its[p]
		var st = _first_byte
		while p >= st and c != u'.' do
			p -= 1
			c = its[p]
		end
		if p <= st then return null
		var ls = last_byte
		return new FlatString.with_infos(its, ls - p, p + 1)
	end
lib/core/file.nit:1363,2--1375,4