Skip to content

Commit

Permalink
Officially support Windows
Browse files Browse the repository at this point in the history
On Windows, MRI Ruby uses the `FindFirstFileW` et. al. kind of functions with
wide character paths and constantly converts from multi-byte to wide character
encoding back and forth when calling any methods on `File` or `Dir`.

This means that when using `Encoding::ASCII_8BIT` for reading directory entries,
that this does not round trip properly, if a file name contains unicode
characters. That is because Ruby assumes the path string is encoded in Windows
Codepage 1252[1].

* skip symlink test if files were checked out from git as textual symlinks on
  Windows
* use UTF-8 encoding on Windows, since this properly round-trips between
  multi-byte and wide character encoding
* travis: windows job no longer is allowed to fail

[1]: https://github.com/ruby/ruby/blob/946cd6c5348d7dd12ecc41e79c3ca9803d2ca7f0/win32/file.c#L131-L134
  • Loading branch information
avdv committed Oct 9, 2020
1 parent 1b659c9 commit 3e88991
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 5 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ jobs:

allow_failures:
- rvm: truffleruby-head
- os: windows

include:
- os: osx
Expand Down
15 changes: 13 additions & 2 deletions lib/colorls/core.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# frozen_string_literal: true

module ColorLS
# on Windows (were the special 'nul' device exists) we need to use UTF-8
@file_encoding = File.exist?('nul') ? Encoding::UTF_8 : Encoding::ASCII_8BIT

def self.file_encoding
@file_encoding
end

class Core
def initialize(input, all: false, report: false, sort: false, show: false,
mode: nil, git_status: false, almost_all: false, colors: [], group: nil,
Expand Down Expand Up @@ -79,7 +86,7 @@ def init_contents(path)
info = FileInfo.new(path, link_info: @long)

if info.directory?
@contents = Dir.entries(path, encoding: Encoding::ASCII_8BIT)
@contents = Dir.entries(path, encoding: ColorLS.file_encoding)

filter_hidden_contents

Expand Down Expand Up @@ -283,14 +290,18 @@ def symlink_info(content)
end
end

def out_encode(str)
str.encode(Encoding.default_external, undef: :replace, replace: '')
end

def fetch_string(path, content, key, color, increment)
@count[increment] += 1
value = increment == :folders ? @folders[key] : @files[key]
logo = value.gsub(/\\u[\da-f]{4}/i) { |m| [m[-4..-1].to_i(16)].pack('U') }
name = content.show
name = make_link(path, name) if @hyperlink
name += content.directory? ? '/' : ' '
entry = "#{logo.encode(Encoding.default_external, undef: :replace, replace: '')} #{name}"
entry = "#{out_encode(logo)} #{out_encode(name)}"

"#{long_info(content)} #{git_info(content)} #{entry.colorize(color)}#{symlink_info(content)}"
end
Expand Down
10 changes: 8 additions & 2 deletions spec/color_ls/flags_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
end

context 'with --sort=time' do
entries = Dir.entries(FIXTURES).grep(/^[^.]/).shuffle.freeze
entries = Dir.entries(FIXTURES, encoding: Encoding::UTF_8).grep(/^[^.]/).shuffle.freeze
mtime = Time.new(2017, 11, 7, 2, 2, 2).freeze

files = entries.each_with_index do |e, i|
Expand Down Expand Up @@ -276,7 +276,13 @@
context 'symlinked directory with trailing separator' do
let(:args) { ['-x', File.join(FIXTURES, 'symlinks', 'Supportlink', File::SEPARATOR)] }

it { expect { subject }.to output(/yaml_sort_checker.rb/).to_stdout }
it 'should show the file in the linked directory' do
if File.symlink? File.join(FIXTURES, 'symlinks', 'Supportlink')
expect { subject }.to output(/yaml_sort_checker.rb/).to_stdout
else
skip "symlinks not supported"
end
end
end

context 'when passing invalid flags' do
Expand Down

0 comments on commit 3e88991

Please sign in to comment.