Skip to content

Commit

Permalink
File Explorer Keyboard Navigation - Part 1 (#1086)
Browse files Browse the repository at this point in the history
* focus -> active

* add separate focus property to state, remove active from TreeView

* add Utility.ArrayEx.findIndex

* add prev/nextExpandedNode to FsTreeNode

* focus previous and next

* use hash for findByPath

* remove FsTreeNode.id, add equals

* fix warnings

* formatting

* add Utility.Path

* abstract hash function

* FileExplorerStore.nodeOffsetByPath -> FsTreeNode.expandedIndex

* select on enter
  • Loading branch information
glennsl authored Dec 19, 2019
1 parent ce8f14f commit bdacaea
Show file tree
Hide file tree
Showing 12 changed files with 421 additions and 214 deletions.
30 changes: 30 additions & 0 deletions src/Core/Utility.re
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,24 @@ module RangeUtil = {
};
};

module ArrayEx = {
exception Found(int);

let findIndex = (predicate, array) =>
try(
{
for (i in 0 to Array.length(array) - 1) {
if (predicate(array[i])) {
raise(Found(i));
};
};
None;
}
) {
| Found(i) => Some(i)
};
};

// TODO: Remove after 4.08 upgrade
module List = {
include List;
Expand Down Expand Up @@ -688,3 +706,15 @@ module ChunkyQueue: {
let toList = ({front, rear, _}) =>
front @ (Queue.toList(rear) |> List.concat);
};

module Path = {
// Not very robust path-handling utilities.
// TODO: Make good

let toRelative = (~base, path) => {
let base = base == "/" ? base : base ++ Filename.dir_sep;
Str.replace_first(Str.regexp_string(base), "", path);
};

let explode = String.split_on_char(Filename.dir_sep.[0]);
};
34 changes: 14 additions & 20 deletions src/Model/FileExplorer.re
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
open Revery;
open Oni_Core;
open Oni_Extensions;

type t = {
tree: option(FsTreeNode.t),
isOpen: bool,
scrollOffset: [ | `Start(float) | `Middle(float)],
focus: option(string) // path
active: option(string), // path
focus: option(string) // node id
};

[@deriving show({with_path: false})]
type action =
| TreeLoaded([@opaque] FsTreeNode.t)
| NodeLoaded(int, [@opaque] FsTreeNode.t)
| FocusNodeLoaded(int, [@opaque] FsTreeNode.t)
| NodeLoaded(string, [@opaque] FsTreeNode.t)
| FocusNodeLoaded(string, [@opaque] FsTreeNode.t)
| NodeClicked([@opaque] FsTreeNode.t)
| ScrollOffsetChanged([ | `Start(float) | `Middle(float)]);

module ExplorerId =
UniqueId.Make({});
| ScrollOffsetChanged([ | `Start(float) | `Middle(float)])
| Select
| FocusPrev
| FocusNext;

let getFileIcon = (languageInfo, iconTheme, filePath) => {
let fileIcon =
Expand Down Expand Up @@ -79,7 +79,6 @@ let getFilesAndFolders = (~ignored, cwd, getIcon) => {
let rec getDirContent = (~loadChildren=false, cwd) => {
let toFsTreeNode = file => {
let path = Filename.concat(cwd, file);
let id = ExplorerId.getUniqueId();

if (isDirectory(path)) {
let%lwt children =
Expand All @@ -94,10 +93,11 @@ let getFilesAndFolders = (~ignored, cwd, getIcon) => {
Lwt.return([]);
};

FsTreeNode.directory(path, ~id, ~icon=getIcon(path), ~children)
|> Lwt.return;
Lwt.return(
FsTreeNode.directory(path, ~icon=getIcon(path), ~children),
);
} else {
FsTreeNode.file(path, ~id, ~icon=getIcon(path)) |> Lwt.return;
FsTreeNode.file(path, ~icon=getIcon(path)) |> Lwt.return;
};
};

Expand All @@ -116,25 +116,19 @@ let getFilesAndFolders = (~ignored, cwd, getIcon) => {
};

let getDirectoryTree = (cwd, languageInfo, iconTheme, ignored) => {
let id = ExplorerId.getUniqueId();
let getIcon = getFileIcon(languageInfo, iconTheme);
let children =
getFilesAndFolders(~ignored, cwd, getIcon)
|> Lwt_main.run
|> List.sort(sortByLoweredDisplayName);

FsTreeNode.directory(
cwd,
~id,
~icon=getIcon(cwd),
~children,
~isOpen=true,
);
FsTreeNode.directory(cwd, ~icon=getIcon(cwd), ~children, ~isOpen=true);
};

let initial = {
tree: None,
isOpen: true,
scrollOffset: `Start(0.),
active: None,
focus: None,
};
12 changes: 8 additions & 4 deletions src/Model/FileExplorer.rei
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ type t = {
tree: option(FsTreeNode.t),
isOpen: bool,
scrollOffset: [ | `Start(float) | `Middle(float)],
focus: option(string) // path
active: option(string), // path
focus: option(string) // node id
};

[@deriving show]
type action =
| TreeLoaded([@opaque] FsTreeNode.t)
| NodeLoaded(int, [@opaque] FsTreeNode.t)
| FocusNodeLoaded(int, [@opaque] FsTreeNode.t)
| NodeLoaded(string, [@opaque] FsTreeNode.t)
| FocusNodeLoaded(string, [@opaque] FsTreeNode.t)
| NodeClicked([@opaque] FsTreeNode.t)
| ScrollOffsetChanged([ | `Start(float) | `Middle(float)]);
| ScrollOffsetChanged([ | `Start(float) | `Middle(float)])
| Select
| FocusPrev
| FocusNext;

let initial: t;

Expand Down
Loading

0 comments on commit bdacaea

Please sign in to comment.