Skip to content

Commit

Permalink
Refactor file type check (lazy and only 1024 bytes) (#30)
Browse files Browse the repository at this point in the history
* Add new binary check
* Add new file type type and use
* Simplify Filec.t
* Fix: use with to make sure it gets closed
  • Loading branch information
heathhenley authored Jan 28, 2025
1 parent 7798895 commit 9c07624
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 56 deletions.
56 changes: 30 additions & 26 deletions lib/fs/filec.ml
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
type t =
| Binary
| Text of {
lines : Pretty.Doc.t array;
offset : int;
}
type t = {
lines : Pretty.Doc.t array;
offset : int;
}

(* Regex to used to determine if bat outputs a binary file warning. This is a
bit of a fragile approach, but there is no robust way to determine if a file
is binary or not. Improve this if it becomes a problem.
*)
let binary_file_pattern = Str.regexp ".*\\[bat warning\\].*Binary.*content*."
type file_type =
| Binary
| Text

let binary_file_warning =
[| Pretty.Doc.str "This file is binary and cannot be displayed" |]

let has_binary_warning contents =
Str.string_match binary_file_pattern contents 0
let open_file_bin num_bytes path =
let buffer = Bytes.create num_bytes in
let n =
In_channel.with_open_bin path (fun ic -> input ic buffer 0 num_bytes)
in
Bytes.sub buffer 0 n

let has_zero_bytes buffer =
let rec has_null = function
| i when i = Bytes.length buffer -> false
| i when Bytes.get buffer i = '\x00' -> true
| i -> has_null (i + 1)
in
has_null 0

let is_likely_binary path = path |> open_file_bin 1024 |> has_zero_bytes

let bat_cmd =
{|bat --style=numbers,changes --color=always --italic-text=always --paging=never --terminal-width=80 |}
Expand All @@ -24,25 +34,19 @@ let read_file_raw path = Shell.proc_stdout (bat_cmd ^ path)

(* Reads file contents using 'bat' to have pretty syntax highlighting *)
let read path =
let contents = read_file_raw path in
if has_binary_warning contents then Binary
if is_likely_binary path then { lines = binary_file_warning; offset = 0 }
else
let lines =
contents
path
|> read_file_raw
|> String.split_on_char '\n'
|> List.map Pretty.Doc.str
|> Array.of_list
in
Text { lines; offset = 0 }

let offset = function
| Text { offset; _ } -> offset
| Binary -> 0
{ lines; offset = 0 }

(* Returns the lines of the file contents *)
let lines = function
| Text { lines; _ } -> lines
| Binary -> binary_file_warning
(* Returns file type based on contents *)
let type_of_path path = if is_likely_binary path then Binary else Text

(* Returns the number of lines in the file contents *)
let length filec = filec |> lines |> Array.length
let length filec = filec.lines |> Array.length
19 changes: 9 additions & 10 deletions lib/fs/filec.mli
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
(** File contents as an array of lines, where each line is wrapped into a
document (for rendering efficiency) *)
type t =
type t = {
lines : Pretty.Doc.t array;
offset : int;
}

type file_type =
| Binary
| Text of {
lines : Pretty.Doc.t array;
offset : int;
}
| Text

(** Reads file contents using 'bat' to have pretty syntax highlighting **)
val read : string -> t

(** Returns offset based on file type of contents **)
val offset : t -> int

(** Returns the len of file contents **)
val length : t -> int

(** Returns the lines of file contents **)
val lines : t -> Pretty.Doc.t array
(** Returns file_type based on the first 1024 bytes of the file **)
val type_of_path : string -> file_type
25 changes: 13 additions & 12 deletions lib/fs/fs.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Filec = Filec

type tree =
| File of string * Filec.t Lazy.t
| File of string * Filec.t Lazy.t * Filec.file_type Lazy.t
| Dir of string * tree array Lazy.t

type dir_cursor = {
Expand All @@ -15,7 +15,7 @@ type cursor =

(* Extracts the file name from a tree node *)
let file_name = function
| File (name, _) -> name
| File (name, _, _) -> name
| Dir (name, _) -> name

(* A files comparison:
Expand All @@ -30,7 +30,7 @@ let order_files t1 t2 =
| _, _ -> String.compare (file_name t1) (file_name t2)

let rec sort_tree = function
| File (name, contents) -> File (name, contents)
| File (name, contents, ft) -> File (name, contents, ft)
| Dir (name, (lazy children)) ->
Array.sort order_files children;
Dir (name, lazy (Array.map sort_tree children))
Expand All @@ -46,7 +46,11 @@ let rec to_tree path =
in
let dirname = Filename.basename path in
Dir (dirname, children)
else File (Filename.basename path, lazy (Filec.read path))
else
File
( Filename.basename path,
lazy (Filec.read path),
lazy (Filec.type_of_path path) )

let read_tree path = path |> to_tree |> sort_tree
let file_at cursor = cursor.files.(cursor.pos)
Expand Down Expand Up @@ -75,13 +79,10 @@ let move_dir_cursor move cursor =
{ cursor with pos = new_pos }

let move_file_cursor move cursor =
match cursor with
| Filec.Binary -> cursor
| Filec.Text txt_cur ->
let len = Filec.length cursor in
let new_offset = Filec.offset cursor + move in
if new_offset < 0 || new_offset + span > len then cursor
else Filec.Text { txt_cur with offset = new_offset }
let len = Filec.length cursor in
let new_offset = cursor.offset + move in
if new_offset < 0 || new_offset + span > len then cursor
else { cursor with offset = new_offset }

let go_move move zipper =
let move_dir = move_dir_cursor move in
Expand All @@ -99,7 +100,7 @@ let go_next zipper =
| Dir_cursor cursor -> (
let next = file_at cursor in
match next with
| File (_name, contents) ->
| File (_name, contents, _) ->
{
parents = cursor :: zipper.parents;
current = File_cursor (Lazy.force contents);
Expand Down
2 changes: 1 addition & 1 deletion lib/fs/fs.mli
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Filec = Filec

(** A definition of a file tree. *)
type tree =
| File of string * Filec.t Lazy.t
| File of string * Filec.t Lazy.t * Filec.file_type Lazy.t
| Dir of string * tree array Lazy.t

(** Return the name of a given tree node. *)
Expand Down
2 changes: 1 addition & 1 deletion lib/tui/tui.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let read_root_tree ~root_dir_path =
let tree = Fs.read_tree root_dir_path in
let files =
match tree with
| Fs.File (path, _) ->
| Fs.File (path, _, _) ->
Printf.printf "Given path '%s' is not a directory!" path;
exit 1
| Fs.Dir (_, files) -> files
Expand Down
12 changes: 6 additions & 6 deletions lib/tui/widget/code.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ let pwd root_dir_path (fs : Fs.zipper) =
Pretty.Doc.(fmt Style.directory full_path)

let file_contents_to_doc ~(file_contents : Fs.Filec.t) =
let lines = Fs.Filec.lines file_contents in
let lines = file_contents.lines in
let len_lines = Array.length lines in
let span = 40 in
let offset = Fs.Filec.offset file_contents in
let offset = file_contents.offset in

let contents_span = Extra.List.of_sub_array ~offset ~len:span lines in

Expand All @@ -58,9 +58,9 @@ let max_file_name_len files =
let fmt_file ~max_name_len (tree : Fs.tree) =
let pad = Extra.String.fill_right max_name_len in
match tree with
| File (name, contents) -> (
match Lazy.force contents with
| Fs.Filec.Text _ -> file_char ^ " " ^ pad name
| File (name, _, file_type) -> (
match Lazy.force file_type with
| Fs.Filec.Text -> file_char ^ " " ^ pad name
| Fs.Filec.Binary -> bin_char ^ " " ^ pad name)
| Dir (name, (lazy children)) -> (
match children with
Expand Down Expand Up @@ -197,7 +197,7 @@ let fs_to_view (fs : Fs.zipper) =
| File_cursor contents, parent :: _ -> (parent, File_selected contents)
| Dir_cursor cursor, _ -> (
match Fs.file_at cursor with
| File (_, contents) -> (cursor, File_selected (Lazy.force contents))
| File (_, contents, _) -> (cursor, File_selected (Lazy.force contents))
| Dir (_, children) ->
( cursor,
Dir_selected
Expand Down

0 comments on commit 9c07624

Please sign in to comment.