Module: Sergeant::Utils

Included in:
SergeantApp
Defined in:
lib/sergeant/utils.rb

Constant Summary collapse

ARCHIVE_EXTENSIONS =
%w[.zip .tar .gz .tgz .bz2 .tbz2 .xz .txz .7z .rar .zst .lz .lzma .cab .iso .deb .rpm .apk
.jar .war].freeze
MEDIA_EXTENSIONS =
%w[.png .jpg .jpeg .gif .bmp .svg .webp .ico .tiff .tif .heic .avif
.mp4 .mkv .avi .mov .wmv .flv .webm .m4v .mpg .mpeg
.mp3 .wav .flac .ogg .aac .m4a .wma .opus].freeze
CODE_EXTENSIONS =
%w[.rb .py .js .jsx .mjs .cjs .ts .tsx .go .rs .c .cpp .cc .h .hpp .java .kt .kts .swift .php
.sh .bash .zsh .fish .lua .pl .pm .r .scala .clj .cljs .ex .exs .erl .hs .ml .mli .sql
.html .htm .css .scss .sass .less .json .yaml .yml .toml .xml .vue .svelte].freeze

Instance Method Summary collapse

Instance Method Details

#file_category(item) ⇒ Object

Which color pair (see Sergeant#apply_color_theme) to use for a file, based on its extension, falling back to the executable bit. Directories are colored separately by draw_screen and never reach this method.



19
20
21
22
23
24
25
26
27
# File 'lib/sergeant/utils.rb', line 19

def file_category(item)
  ext = File.extname(item[:name]).downcase
  return :archive if ARCHIVE_EXTENSIONS.include?(ext)
  return :media if MEDIA_EXTENSIONS.include?(ext)
  return :code if CODE_EXTENSIONS.include?(ext)
  return :executable if item[:mode] && (item[:mode] & 0o111).nonzero? && ext.empty?

  :file
end

#format_date(time) ⇒ Object



112
113
114
115
116
# File 'lib/sergeant/utils.rb', line 112

def format_date(time)
  return '' if time.nil?

  time.strftime('%b %d %H:%M')
end

#format_permissions(mode, is_directory) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sergeant/utils.rb', line 77

def format_permissions(mode, is_directory)
  type = is_directory ? 'd' : '-'

  owner = ''
  owner += mode.anybits?(0o400) ? 'r' : '-'
  owner += mode.anybits?(0o200) ? 'w' : '-'
  owner += mode.anybits?(0o100) ? 'x' : '-'

  group = ''
  group += mode.anybits?(0o040) ? 'r' : '-'
  group += mode.anybits?(0o020) ? 'w' : '-'
  group += mode.anybits?(0o010) ? 'x' : '-'

  other = ''
  other += mode.anybits?(0o004) ? 'r' : '-'
  other += mode.anybits?(0o002) ? 'w' : '-'
  other += mode.anybits?(0o001) ? 'x' : '-'

  "#{type}#{owner}#{group}#{other}"
end

#format_size(bytes) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/sergeant/utils.rb', line 98

def format_size(bytes)
  return '     ' if bytes.nil?

  if bytes < 1024
    "#{bytes}B".rjust(7)
  elsif bytes < 1024 * 1024
    "#{(bytes / 1024.0).round(1)}K".rjust(7)
  elsif bytes < 1024 * 1024 * 1024
    "#{(bytes / (1024.0 * 1024)).round(1)}M".rjust(7)
  else
    "#{(bytes / (1024.0 * 1024 * 1024)).round(1)}G".rjust(7)
  end
end

#fzf_available?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/sergeant/utils.rb', line 118

def fzf_available?
  system('command -v fzf > /dev/null 2>&1')
end

#get_git_branchObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/sergeant/utils.rb', line 45

def get_git_branch
  return nil unless Dir.exist?(File.join(@current_dir, '.git'))

  head_file = File.join(@current_dir, '.git', 'HEAD')
  return nil unless File.exist?(head_file)

  head_content = File.read(head_file).strip
  if head_content.start_with?('ref: refs/heads/')
    head_content.sub('ref: refs/heads/', '')
  else
    head_content[0..7]
  end
rescue StandardError
  nil
end

#get_owner_info(stat) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/sergeant/utils.rb', line 61

def get_owner_info(stat)
  begin
    user = Etc.getpwuid(stat.uid).name
  rescue StandardError
    user = stat.uid.to_s
  end

  begin
    group = Etc.getgrgid(stat.gid).name
  rescue StandardError
    group = stat.gid.to_s
  end

  "#{user}:#{group}"
end

#glow_available?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/sergeant/utils.rb', line 122

def glow_available?
  system('command -v glow > /dev/null 2>&1')
end

#nano_available?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/sergeant/utils.rb', line 138

def nano_available?
  system('command -v nano > /dev/null 2>&1')
end

#nvim_available?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/sergeant/utils.rb', line 126

def nvim_available?
  system('command -v nvim > /dev/null 2>&1')
end

#text_file?(file_path) ⇒ Boolean

Returns:

  • (Boolean)


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/sergeant/utils.rb', line 142

def text_file?(file_path)
  return false unless File.file?(file_path)
  return false unless File.readable?(file_path)

  # Check file size - limit to 50MB for safety
  return false if File.size(file_path) > 50 * 1024 * 1024

  # Check if it's a binary file by reading first 8KB
  File.open(file_path, 'rb') do |f|
    sample = f.read(8192) || ''
    # Check for null bytes (common in binary files)
    !sample.bytes.include?(0)
  end
rescue StandardError
  false
end

#token_category(qualname) ⇒ Object

Maps a Rouge token's qualified name (e.g. "Literal.String.Double") to one of the syntax-highlight color pairs set up in Sergeant#apply_color_theme. Unmatched token types (Text, Punctuation, Operator, ...) return nil, meaning "draw in the default text color".



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/sergeant/utils.rb', line 33

def token_category(qualname)
  case qualname
  when /\AComment/ then :comment
  when /\AKeyword/ then :keyword
  when /\ALiteral\.String/ then :string
  when /\ALiteral\.Number/ then :number
  when /\AName\.(Function|Class|Namespace|Tag)/ then :function
  when /\AName\.(Constant|Builtin|Variable\.Class)/ then :constant
  when /\A(Error|Generic\.Error|Generic\.Traceback)/ then :error
  end
end

#vi_available?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/sergeant/utils.rb', line 134

def vi_available?
  system('command -v vi > /dev/null 2>&1')
end

#vim_available?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/sergeant/utils.rb', line 130

def vim_available?
  system('command -v vim > /dev/null 2>&1')
end