忍者ブログ

cc-memo

Computer Craft のメモ

filer(ファイラー)

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

コメント

ただいまコメントを受けつけておりません。

filer(ファイラー)

標準の ls が遅いので、単に早いだけの ls を作ろうとしていたはずだったのだが、ついつい機能を追加してしまい、ファイラーのようなものになってしまったので、filerという名前にした。
※どうでもいいけど、得られる便利さに見合わないぐらいソースが長い気がする、、、

画像で説明

filerコマンドを実行すると、カレントディレクトリのファイルとディレクトリが、画像のように表示される。
・qキーを押すと、filerコマンドを終了する。
・カーソルの上下でページを切り替えることができる。
 ⇒5ページ目を表示した後で、4ページ目に戻る、という操作が可能。
・この状態でカーソルキーの右を押すと、、、


画像のように、ファイル/ディレクトリを選択する矢印(-->)が表示される。
・カーソルキーの左を押すと、最初の状態(矢印が表示されない状態)に戻る。
・カーソルキーの右を押すことで、矢印の左側にある”アクション”を切り替える事ができる。
 以下の”アクション”が選択できる。
 ・cd   ディレクトリ移動(選択項目がディレクトリの場合)
 ・edit  選択したファイルを edit コマンドで開く(選択項目がファイルの場合)
 ・delete 選択したファイルを削除する
 ・rename 選択したファイルのファイル名を変更する
 ・copy  選択したファイルをコピーする。(同一ディレクトリ内でのコピー専用)
 ・move  選択したファイルを移動、または、コピーする。
  (別のディレクトリに移動/コピーする場合に使用)
 ・[other] 任意のコマンドを実行する。
   コマンドを入力する場面で、カーソルキーの上を押すと、
   選択したファイル/ディレクトリ名が表示され、それを編集できるので、少し楽かも。


moveの場合は、まず最初に、移動、または、コピーしたいファイルかディレクトリを選択し、、、


次に、移動/コピー先フォルダを選択。
cd --> でフォルダを移動し、移動/コピー先を指定する場合には、
moveTo --> に切り替えて、enterキーを押す。
なお、
・「<dir> ..」は1階層上のフォルダ
・「<dir> .」は現在のフォルダ
を示している。


あとは、コピーか移動かの選択、コピー/移動先の選び直し、コピー/移動をやめるといった選択が可能。

※ほかの”アクション”の説明は省略。

以下ソース
filer
local function isOneOf(val, possibleValues)
  for _, v in ipairs(possibleValues) do
    if val == v then
      return true;
    end
  end
  return false;
end

local function startsWith(subject, prefix)
  if subject:len() < prefix:len() then
    return false;
  end
  for i = 1, prefix:len() do
    if subject:sub(i, i) ~= prefix:sub(i, i) then
      return false;
    end
  end
  return true;
end

local function makePageData(width, height, directoryOnlyFlg)
  local rawLst = fs.list(shell.dir());
  local fLst = { };
  local dLst = { };
  for _, val in ipairs(rawLst) do
    if fs.isDir(shell.dir() .. "/" .. val) then
      table.insert(dLst, { name = val, isDir = true });
    else
      table.insert(fLst, { name = val, isDir = false });
    end
  end
  function comp(a, b)
    return a.name < b.name;
  end
  local lst = { };

  if "" ~= shell.dir() then
    table.insert(lst, { name = "..", isDir = true });
  end

  if directoryOnlyFlg then
    table.insert(lst, { name = ".", isDir = true });
  end

  table.sort(dLst, comp);
  for _, val in ipairs(dLst) do
    table.insert(lst, val);
  end

  if not directoryOnlyFlg then
    table.sort(fLst, comp);
    for _, val in ipairs(fLst) do
      table.insert(lst, val);
    end
  end

  local pages = { };
  local currPage = { };
  for _, val in ipairs(lst) do
    table.insert(currPage, val);
    if height - 1 <= #currPage then
      table.insert(pages, currPage);
      currPage = { };
    end
  end
  if 0 < #currPage then
    table.insert(pages, currPage);
  end
  return pages;
end

local function resetTerm()
  term.setBackgroundColor(colors.black);
  term.setTextColor(colors.white);
  term.clear();
  term.setCursorPos(1, 1);
end

local function prepareExit(initialDir)
  resetTerm();
  shell.setDir(initialDir);
end

local function pagingAction(isUp, pageMode, pageIdx, lineIdx, pages, newNotice)

  if isUp then
    if pageMode then
      pageIdx = math.max(pageIdx - 1, 1);
    else
      if lineIdx - 1 <= 0 then
        if 2 <= pageIdx then
          pageIdx = pageIdx - 1;
          lineIdx = #pages[pageIdx];
        end
      else
        lineIdx = lineIdx - 1;
      end
    end

  else
    if pageMode then
      pageIdx = math.min(pageIdx + 1, #pages);
    else
      if #pages[pageIdx] < lineIdx + 1 then
        if pageIdx <= #pages - 1 then
          pageIdx = pageIdx + 1;
          lineIdx = 1;
        end
      else
        lineIdx = lineIdx + 1;
      end
    end
  end

  local elm = pages[pageIdx][lineIdx];
  if not pageMode then
    if elm.isDir and ".." == elm.name then
      newNotice = ".. means parent directory.";
    elseif elm.isDir and "." == elm.name then
      newNotice = ". means current directory.";
    end
  end

  return pageIdx, lineIdx, newNotice;
end

local function getParentPath(directoryPath)
  local tmp = directoryPath:reverse();
  return tmp:sub(tmp:find("/", 1, true) + 1):reverse();
end

local function ascendDirectory()
  if "" == shell.dir() then
    return false, "";

  else
    returnTrip = true;
    shell.setDir(getParentPath(shell.dir()));

    local newNotice;
    if "" == shell.dir() then
      newNotice = "ascended. /";
    else
      newNotice = "ascended. " .. shell.dir();
    end
    return true, newNotice;
  end
end

local function lineModeAction(currentAction, currentSubject, 
                              pathIndexStack, pageIdx_, lineIdx_,
                              toolIdx, moveToMode, moveSrcPath)
  local directoryChanged = false;
  local returnTrip = false;
  local newNotice = "";

  if startsWith(currentAction, "edit") then
    shell.run("edit", currentSubject);

  elseif startsWith(currentAction, "cd") then
    if ".." == currentSubject then
      returnTrip, newNotice = ascendDirectory();

    else
      table.insert(pathIndexStack, { pageIdx = pageIdx_, lineIdx = lineIdx_ });
      shell.setDir(shell.dir() .. "/" .. currentSubject);
      newNotice = "descended. " .. shell.dir();
    end
    directoryChanged = true;

  elseif startsWith(currentAction, "delete") then
    if "." == currentSubject or ".." == currentSubject then
      newNotice = currentSubject .. " cannot delete.";
    else
      resetTerm();
      print("[delete] " .. currentSubject);
      print("Do you really want to delete this file?");
      term.write("[y]es / [n]o >");
      if "y" == read() then
        shell.run("delete", currentSubject);
        newNotice = currentSubject .. " was (maybe) deleted.";
        directoryChanged = true;

      else
        newNotice = "delete canceled.";
      end
    end

  elseif startsWith(currentAction, "rename") then
    if "." == currentSubject or ".." == currentSubject then
      newNotice = currentSubject .. " cannot rename.";
    else
      resetTerm();
      print("[rename] " .. currentSubject);
      print("Enter new name.");
      print("To borrow original name, press [up] cursor key.");
      print("If you want to cancel renaming, enter blank.");
      term.write(">");
      local newName = read(nil, { currentSubject });
      if "" ~= newName then
        shell.run("rename", currentSubject, newName);
        newNotice = currentSubject .. " was (maybe) renamed.";
        directoryChanged = true;

      else
        newNotice = "rename canceled.";
      end
    end

  elseif startsWith(currentAction, "copy") then
    if "." == currentSubject or ".." == currentSubject then
      newNotice = currentSubject .. " cannot copy.";
    else
      resetTerm();
      print("[copy] " .. currentSubject);
      print("Enter new name.");
      print("To borrow original name, press [up] cursor key.");
      print("If you want to cancel copy, enter blank.");
      term.write(">");
      local newName = read(nil, { currentSubject });
      if "" ~= newName then
        shell.run("copy", currentSubject, newName);
        newNotice = currentSubject .. " was (maybe) copied.";
        directoryChanged = true;

      else
        newNotice = "copy canceled.";
      end
    end

  elseif      startsWith(currentAction, "move")
      and not startsWith(currentAction, "moveTo") then
    if "." == currentSubject or ".." == currentSubject then
      newNotice = currentSubject .. " cannot copy.";
    else
      moveToMode = true;
      newNotice = "<<move>> select moveTo directory.";
      directoryChanged = true;
      moveSrcPath = shell.dir() .. "/" .. currentSubject;
      pageIdx_ = 1;
      lineIdx_ = 1;
      toolIdx = 1;
    end

  elseif startsWith(currentAction, "cancel move") then
    moveToMode = false;
    newNotice = "move canceled.";
    directoryChanged = true;
    pageIdx_ = 1;
    lineIdx_ = 1;
    toolIdx = 1;

  elseif startsWith(currentAction, "moveTo") then
    local choice = "move / copy";
    local wrong = false;
    local moveDstPath;
    if "." == currentSubject then
      moveDstPath = shell.dir();
    elseif ".." == currentSubject then
      moveDstPath = getParentPath(shell.dir());
    else
      moveDstPath = shell.dir() .. "/" .. currentSubject;
    end

    while true do
      resetTerm();
      print("[" .. choice .. "]");
      print("source      :");
      print("  " .. moveSrcPath);
      print("destination :");
      print("  " .. moveDstPath);

      print("select move or copy.");
      if wrong then
        wrong = false;
        print("*** Wrong input. Please retry.");
      end
      print(" copy : input \"c\" and hit enter key.");
      print(" move : hit enter key with no input.");
      print(" or [r]eselect destination directory.");
      print(" or [q]uit. (cancel move or copy.)");
      term.write(">");
      local input = read();
      if "c" == input then
        choice = "copy";
        break;
      elseif "" == input then
        choice = "move";
        break;
      elseif "r" == input then
        choice = "reselect";
        break;
      elseif "q" == input then
        choice = "quit";
        break;
      else
        wrong = true;
      end
    end
    if "copy" == choice then
      shell.run("copy", moveSrcPath, moveDstPath);
      newNotice = "The copy was successful maybe.";
    elseif "move" == choice then
      shell.run("move", moveSrcPath, moveDstPath);
      newNotice = "The move was successful maybe.";
    elseif "reselect" == choice then
      newNotice = "<<move>> select moveTo directory.";
      toolIdx = 1;
    elseif "quit" == choice then
      newNotice = "copy canceled.";
    end

    if "reselect" ~= choice then
      moveToMode = false;
      directoryChanged = true;
      pageIdx_ = 1;
      lineIdx_ = 1;
      toolIdx = 1;
    end

  elseif startsWith(currentAction, "[other]") then
    if "." == currentSubject or ".." == currentSubject then
      newNotice = currentSubject .. " cannot [other].";
    else
      resetTerm();
      local stage = 1;
      local input = "";
      local ret;
      while true do
        print("[other]");
        local hist = { };
        table.insert(hist, currentSubject);
        table.insert(hist, currentSubject .. " " .. currentSubject);
        if "" ~= input then
          table.insert(hist, input);
        end

        if 1 == stage then
          print("Input command.");
          term.write(">");
          input = read(nil, hist);
          if "" == input then
            newNotice = "[other] canceled.";
            break;
          else
            stage = 2;
          end
        elseif 2 == stage then
          print("command = ");
          print(input);
          print("");
          print("execute? [y]es, [e]dit, [c]ancel");
          term.write(">");
          local ch = read();
          if "y" == ch then
            ret = shell.run(input);
            newNotice = "[other] returned " .. tostring(ret);
            break;
          elseif "e" == ch then
            stage = 1;
          elseif "c" == ch then
            newNotice = "[other] canceled.";
            break;
          end
        end
      end
    end
  end
  return directoryChanged, pathIndexStack, returnTrip, newNotice, moveToMode,
         pageIdx_, lineIdx_, toolIdx, moveSrcPath;
end

local SPACE = " ";
local pageIdx = 1;
local lineIdx = 1;
local toolIdx = 1;
local pageMode = true;
local tools = { dir  = { "cd   --> ", "delete --> ", "rename --> ",
                         "copy --> ", "move --> ", "[other] --> "},
                file = { "edit --> ", "delete --> ", "rename --> ",
                         "copy --> ", "move --> ", "[other] --> "},
                move = { "cd --> ", "moveTo --> ", "cancel move " } };
local directoryChanged;
local pages;
local width, height = term.getSize();
local returnTrip = false;
local pathIndexStack = { };
local initialDir = shell.dir();
local miniHelp = { true_  = "[q]uit, up/down=paging, [->]=line mode",
                   false_ = "[->]=change action, enter=exec action" };
local statusBarText = "";
local newNotice = "";
local moveToMode = false;
local directoryOnlyFlg = false;
local moveSrcPath = "";

local KNtC = { };

for i = 0, 256 do
  local name = keys.getName(i);
  if nil ~= name then
    KNtC[name] = i;
  end
end

directoryChanged = true;
while true do

  if moveToMode then
    directoryOnlyFlg = true;
  else
    directoryOnlyFlg = false;
  end

  if directoryChanged then
    pages = makePageData(width, height, directoryOnlyFlg);
    pageIdx = math.min(#pages, pageIdx);
    lineIdx = math.min(#pages[pageIdx], lineIdx);
    if returnTrip and #pathIndexStack then
      local o = table.remove(pathIndexStack);
      pageIdx = o.pageIdx;
      lineIdx = o.lineIdx;
    end
    returnTrip = false;
    directoryChanged = false;
  end

  local currentLineIsDir;
  local currentAction = "";
  local currentSubject;
  local toolSetKey;

  resetTerm();
  for i, val in ipairs(pages[pageIdx]) do
    term.setCursorPos(1, i);
    local arrow = "";
    if not pageMode then
      lineIdx = math.min(lineIdx, #pages[pageIdx]);

      if moveToMode then
        toolSetKey = "move";
      else
        if val.isDir then
          toolSetKey = "dir";
        else
          toolSetKey = "file";
        end
      end

      if i == lineIdx then
        arrow = tools[toolSetKey][toolIdx];
        currentAction = arrow;
        currentLineIsDir = val.isDir;
        currentSubject = val.name;
      else
        arrow = SPACE:rep(#tools[toolSetKey][toolIdx]);
      end
    end

    if val.isDir then
      term.write(arrow .. "<dir> " .. val.name
                 .. SPACE:rep(math.max(0, width - (#arrow + #val.name))));
    else
      term.write(arrow .. val.name
                 .. SPACE:rep(math.max(0, width - (#arrow + #val.name))));
    end
  end
  for i = #pages[pageIdx] + 1, height do
    term.setCursorPos(1, i);
    term.write(SPACE:rep(width));
  end

  term.setCursorPos(1, height);
  term.setTextColor(colors.black);
  term.setBackgroundColor(colors.white);

  if not moveToMode then
    if "" ~= newNotice then
      statusBarText = newNotice;
      newNotice = "";
    else
      statusBarText = miniHelp[tostring(pageMode) .. "_"];
    end
  else
    statusBarText = newNotice;
  end
  term.write(statusBarText .. SPACE:rep(math.max(0, width - #statusBarText)));

  local easyExitFlg = false;
  while true do
    local event, scanCode = os.pullEvent("key");

    -- key name 
    -- http://computercraft.info/wiki/Keys_(API)
    if isOneOf(scanCode, { KNtC.up, KNtC.p, KNtC.b, KNtC.w, KNtC.a }) then
      pageIdx, lineIdx, newNotice
        = pagingAction(true,  pageMode, pageIdx, lineIdx, pages, newNotice);
      break;

    elseif isOneOf(scanCode,
             { KNtC.down, KNtC.n, KNtC.f, KNtC.s, KNtC.d, KNtC.space }) then
      pageIdx, lineIdx, newNotice
        = pagingAction(false, pageMode, pageIdx, lineIdx, pages, newNotice);
      break;

    elseif isOneOf(scanCode, { KNtC.right }) then
      if not pageMode then
        toolIdx = (toolIdx % #tools[toolSetKey]) + 1;
        if startsWith(
            tools[toolSetKey][toolIdx], "copy") then
          newNotice = "The copy on the same directory.";

        elseif      startsWith(tools[toolSetKey][toolIdx], "move")
            and not startsWith(tools[toolSetKey][toolIdx], "moveTo") then
          newNotice = "Move (or copy) to other directory.";

        elseif startsWith(tools[toolSetKey][toolIdx], "[other]") then
          newNotice = "A command can be executed manually.";
        end
      end
      pageMode = false;
      break;

    elseif isOneOf(scanCode, { KNtC.left }) then
      toolIdx = 1;
      if pageMode then
        returnTrip, newNotice = ascendDirectory();
        directoryChanged = returnTrip;
      end
      if not moveToMode then
        pageMode = true;
      end
      break;

    elseif isOneOf(scanCode, { KNtC.q, KNtC.e }) then
      prepareExit(initialDir);
      return;

    elseif isOneOf(scanCode, { KNtC.enter }) then
      directoryChanged, pathIndexStack, returnTrip, newNotice, moveToMode,
      pageIdx, lineIdx, toolIdx, moveSrcPath
        = lineModeAction(currentAction, currentSubject, pathIndexStack,
                         pageIdx, lineIdx, toolIdx, moveToMode, moveSrcPath);
      break;
    end
    sleep(0.5);
  end
end
PR

コメント

プロフィール

HN:
kssr
性別:
非公開

Twitter

最古記事

(04/25)
(04/26)
(04/27)
(04/28)
(04/29)

アンテナ・ランキング