Escher-js / SkimmedMilk

0 stars 0 forks source link

show & select branch commit #16

Closed psmuler closed 1 year ago

psmuler commented 1 year ago

ブランチを切り替えたら、そのブランチの最新のコミットを取得してファイルを表示するようにしましょう

psmuler commented 1 year ago

ブランチを切り替えた際に、そのブランチの最新のコミットを取得し、ファイルを表示するようにするには、以下の手順に従います。

  1. ブランチ選択プルダウンのイベントリスナを追加します。
  2. 選択されたブランチにチェックアウトします。
  3. 最新のコミットからファイルを読み込み、テキストエディタに表示します。

renderer.jsに以下のコードを追加して、上記の手順を実行します。

// ブランチ選択時の処理
branchSelect.addEventListener('change', async () => {
  const selectedBranch = branchSelect.options[branchSelect.selectedIndex].value;
  const folderPath = folderPathSpan.textContent;

  // 選択されたブランチにチェックアウト
  await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" checkout "${selectedBranch}"`, (error, stdout, stderr) => {
      if (error) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
        reject(new Error(stderr));
        return;
      }
      console.log(`Branch switched: ${stdout}`);
      resolve();
    });
  });

  // 最新のコミットからファイルを読み込み
  const fileContent = await new Promise((resolve, reject) => {
    fs.readFile(`${folderPath}/${currentFileName}`, 'utf-8', (error, data) => {
      if (error) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      resolve(data);
    });
  });

  // テキストエディタにファイル内容を表示
  textEditor.value = fileContent;
});

このコードは、ブランチ選択プルダウンでブランチが選択されたときに実行されるイベントリスナを追加します。選択されたブランチにチェックアウトし、最新のコミットからファイルを読み込んでテキストエディタに表示します。

psmuler commented 1 year ago

Stderr: Switched to branch 'main'

(anonymous) @ renderer.js:41 renderer.js:42 Uncaught (in promise) Error: Switched to branch 'main'

at renderer.js:42:24
at ChildProcess.exithandler (node:child_process:422:7)
at ChildProcess.emit (node:events:513:28)
at maybeClose (node:internal/child_process:1091:16)
at Socket.<anonymous> (node:internal/child_process:449:11)
at Socket.emit (node:events:513:28)
at Pipe.<anonymous> (node:net:322:12)

ブランチの切り替え時に発生するメッセージがエラー扱いのため、先に進めません。

psmuler commented 1 year ago

Switched to branch 'main'メッセージがstderrで出力されるのは想定外ですが、このメッセージはエラーではなく、実際には成功メッセージです。この問題を解決するために、execstderrに対して条件を追加して、特定の成功メッセージをエラーとして扱わないようにしましょう。

renderer.js内のexec関数を以下のように書き換えてください。

// 選択されたブランチにチェックアウト
await new Promise((resolve, reject) => {
  exec(`git -C "${folderPath}" checkout "${selectedBranch}"`, (error, stdout, stderr) => {
    if (error) {
      console.error(`Error: ${error.message}`);
      reject(error);
      return;
    }
    if (stderr && !stderr.startsWith('Switched to branch')) {
      console.error(`Stderr: ${stderr}`);
      reject(new Error(stderr));
      return;
    }
    console.log(`Branch switched: ${stdout}${stderr}`);
    resolve();
  });
});

これにより、stderrの内容がSwitched to branchで始まる場合、エラーとして扱わなくなります。それ以外の場合は、従来通りエラーとして扱われます。

psmuler commented 1 year ago

Stderr: Switched to branch 'main'

(anonymous) @ renderer.js:41 renderer.js:42 Uncaught (in promise) Error: Switched to branch 'main'

at renderer.js:42:24
at ChildProcess.exithandler (node:child_process:422:7)
at ChildProcess.emit (node:events:513:28)
at maybeClose (node:internal/child_process:1091:16)
at Socket.<anonymous> (node:internal/child_process:449:11)
at Socket.emit (node:events:513:28)
at Pipe.<anonymous> (node:net:322:12)

ブランチの切り替え時に発生するメッセージがエラー扱いのため、先に進めません。exit status が 0かどうかなどをもとにエラーでないかどうか判断するようにできますか。

psmuler commented 1 year ago

確かに、Switched to branch 'main'のメッセージはエラーではなく、正常なメッセージです。この問題を解決するために、execのコールバック関数内でerrorオブジェクトのcodeプロパティをチェックし、それが0であるかどうかを確認しましょう。codeが0であれば、エラーとはみなさずに処理を続行します。

renderer.jsのブランチ選択時の処理を以下のように修正してください。

// ブランチ選択時の処理
branchSelect.addEventListener('change', async () => {
  const selectedBranch = branchSelect.options[branchSelect.selectedIndex].value;
  const folderPath = folderPathSpan.textContent;

  // 選択されたブランチにチェックアウト
  await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" checkout "${selectedBranch}"`, (error, stdout, stderr) => {
      if (error && error.code !== 0) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
      }
      console.log(`Branch switched: ${stdout}`);
      resolve();
    });
  });

  // 最新のコミットからファイルを読み込み
  const fileContent = await new Promise((resolve, reject) => {
    fs.readFile(`${folderPath}/${currentFileName}`, 'utf-8', (error, data) => {
      if (error) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      resolve(data);
    });
  });

  // テキストエディタにファイル内容を表示
  textEditor.value = fileContent;
});

この修正により、エラーが発生しているかどうかを正しく判断できるようになります。

psmuler commented 1 year ago

branchSelect.addEventListener('change', async () => {のif(branchSelect.value === 'create-new-branch')でipcRenderer.invoke('show-branch-input-dialog');を待っている間にもそれ以下のコードが実行されてしまいエラーになってしまいます。 await new Promise((resolve, reject) => { exec(git -C "${folderPath}" checkout "${selectedBranch}", (error, stdout, stderr) => { if (error && error.code !== 0) { console.error(Error: ${error.message}); reject(error); return; } if (stderr) { console.error(Stderr: ${stderr}); } console.log(Branch switched: ${stdout}); resolve(); }); });

psmuler commented 1 year ago

branchSelect.addEventListener('change', async () => {の処理でipcRenderer.invoke('show-branch-input-dialog');を待機するように修正するために、changeイベントリスナーの中でbranchSelect.value === 'create-new-branch'の条件が満たされた場合、Promiseを返す関数を定義してawaitを使って待機しましょう。

以下のようにコードを修正してください。

// ブランチ選択時の処理
branchSelect.addEventListener('change', async () => {
  const selectedBranch = branchSelect.options[branchSelect.selectedIndex].value;
  const folderPath = folderPathSpan.textContent;

  if (selectedBranch === 'create-new-branch') {
    // show-branch-input-dialogを待機するPromiseを定義
    const waitForBranchInputDialog = async () => {
      return new Promise((resolve) => {
        ipcRenderer.once('branch-input-dialog-closed', () => {
          resolve();
        });
      });
    };

    // ダイアログが閉じるのを待つ
    await ipcRenderer.invoke('show-branch-input-dialog');
    await waitForBranchInputDialog();

    // ブランチリストを更新
    await updateBranchList();
    return;
  }

  // 選択されたブランチにチェックアウト
  await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" checkout "${selectedBranch}"`, (error, stdout, stderr) => {
      if (error && error.code !== 0) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
      }
      console.log(`Branch switched: ${stdout}`);
      resolve();
    });
  });

  // 最新のコミットからファイルを読み込み
  const fileContent = await new Promise((resolve, reject) => {
    fs.readFile(`${folderPath}/${currentFileName}`, 'utf-8', (error, data) => {
      if (error) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      resolve(data);
    });
  });

  // テキストエディタにファイル内容を表示
  textEditor.value = fileContent;
});

この修正により、branch-input-dialog-closedイベントが発生するまで、後続のコードの実行が待機されます。これでエラーが発生しなくなります。

psmuler commented 1 year ago

できました。 追跡しているブランチのコミットを右側ペインを作成し、表示する機能をつけましょう

psmuler commented 1 year ago

まず、index.htmlに新たなペインを作成し、コミット一覧を表示する領域を作りましょう。

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Electron App</title>
  </head>
  <body>
    <!-- 省略 -->

    <div id="main">
      <textarea id="text-editor"></textarea>
      <!-- 追加: コミット一覧を表示する右側ペイン -->
      <div id="commit-list-pane">
        <h3>Commit List</h3>
        <ul id="commit-list"></ul>
      </div>
    </div>

    <script src="renderer.js"></script>
  </body>
</html>

次に、renderer.jsに、選択されたブランチのコミット一覧を表示する関数を作成しましょう。

renderer.js:

// 省略

// 選択されたブランチのコミット一覧を表示
async function showCommitList() {
  const selectedBranch = branchSelect.options[branchSelect.selectedIndex].value;
  const folderPath = folderPathSpan.textContent;

  if (!folderPath || selectedBranch === 'create-new-branch') {
    return;
  }

  const commitLogOutput = await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" log --pretty=format:"%h - %s" ${selectedBranch}`, (error, stdout, stderr) => {
      if (error && error.code !== 0) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
      }
      resolve(stdout);
    });
  });

  const commitList = document.getElementById('commit-list');
  commitList.innerHTML = '';

  const commitLines = commitLogOutput.split('\n');
  commitLines.forEach((commitLine) => {
    const listItem = document.createElement('li');
    listItem.textContent = commitLine;
    commitList.appendChild(listItem);
  });
}

// 省略

branchSelect.addEventListener('change', async () => {
  // 省略
  // ブランチを切り替えた後、コミット一覧を表示
  await showCommitList();
});

このコードは、選択されたブランチのコミット一覧を取得し、コミットのハッシュとサブジェクト(1行の説明)を表示します。branchSelectイベントリスナーの中でshowCommitList()関数を呼び出すことで、ブランチを切り替えるたびにコミット一覧が更新されるようになります。

追加で、CSSを適用してコミット一覧ペインとテキストエディタが横並びになるようにしましょう。

styles.css:

/* 省略 */

#main {
  display: flex;
  width: 100%;
}
#text-editor {
  flex: 1;
  resize: none;
  width: 100%;
  height: 100%;
}

次に、コミット一覧ペインにスタイルを追加して、テキストエディタと横並びに表示されるようにしましょう。

styles.css:

#commit-list-pane {
  width: 300px;
  padding: 1rem;
  border-left: 1px solid #ccc;
}

#commit-list {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

これで、テキストエディタとコミット一覧ペインが横並びに表示され、ブランチを切り替えるたびにコミット一覧が更新されるようになります。

psmuler commented 1 year ago

index.htmlでstyles.cssをインポートしましょう

psmuler commented 1 year ago

index.htmlstyles.cssをインポートするには、<head>タグ内に<link>タグを追加します。

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Markdown Git Editor</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <!-- 以前のコード -->
  </body>
</html>

このようにして、styles.cssindex.htmlにインポートすることができます。これにより、定義したスタイルが適用されるようになります。

psmuler commented 1 year ago

text-editorの高さが小さくなってしまいました

psmuler commented 1 year ago

すみません、それは私のミスです。#text-editorのスタイルを修正しましょう。height: 100%; を削除し、flex-grow: 1; を追加してください。これにより、テキストエディタが残りの空間を占めるようになります。

styles.css:

#text-editor {
  flex-grow: 1;
  resize: none;
  width: 100%;
}

また、#containerdisplay: flex;を追加して、テキストエディタとコミット一覧ペインが横並びに表示されるようにしましょう。

styles.css:

#container {
  display: flex;
}

これでテキストエディタの高さが適切になり、画面全体に広がるはずです。

psmuler commented 1 year ago

表示が大きくなるようになりましたが、コミットリストが表示されていない状態でもテキストボックス全体が表示されるようにしましょう

psmuler commented 1 year ago

すみません、その問題を修正しましょう。#containerのスタイルにflex-grow: 1;を追加し、コンテナが親要素に対して適切にリサイズされるようにします。

また、bodyタグのスタイルにdisplay: flex; flex-direction: column; height: 100vh;を追加して、コンテナが縦方向に画面全体に広がるようにしましょう。

styles.css:

body {
  margin: 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
}

#container {
  display: flex;
  flex-grow: 1;
}

これで、コミットリストが表示されていない状態でも、テキストボックスが画面全体に広がるようになります。

psmuler commented 1 year ago

containerというタグはありません

psmuler commented 1 year ago

すみません、#containerに言及したのは私のミスです。代わりに、以下のようにindex.html<body>タグに#app-containerというIDを追加しましょう。

index.html:

<body>
  <div id="app-container">
    <!-- 以前のコード -->
  </div>
</body>

そして、styles.cssに新しく追加した#app-containerに対して、display: flex;のスタイルを適用しましょう。

styles.css:

#app-container {
  display: flex;
}

これで、テキストエディタとコミット一覧ペインが横並びに表示されるようになります。

psmuler commented 1 year ago

各コミットを選択できるようにし、選ばれたらそのコミットをエディタに表示するようにしましょう

psmuler commented 1 year ago

以下のようにコードを修正し、各コミットを選択できるようにし、選ばれたらそのコミットをエディタに表示するようにしましょう。

renderer.js:

// 省略

// 選択されたコミットの内容を表示
async function showSelectedCommit(commitHash) {
  const folderPath = folderPathSpan.textContent;

  const fileContent = await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" show ${commitHash}:${mdFileName}`, (error, stdout, stderr) => {
      if (error && error.code !== 0) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
      }
      resolve(stdout);
    });
  });

  const textEditor = document.getElementById('text-editor');
  textEditor.value = fileContent;
}

// 省略

// 選択されたブランチのコミット一覧を表示
async function showCommitList() {
  // 省略

  const commitLines = commitLogOutput.split('\n');
  commitLines.forEach((commitLine) => {
    const listItem = document.createElement('li');
    listItem.textContent = commitLine;
    listItem.addEventListener('click', () => {
      const commitHash = commitLine.split(' ')[0];
      showSelectedCommit(commitHash);
    });
    commitList.appendChild(listItem);
  });
}

// 省略

このコードでは、各リスト項目にclickイベントリスナーを追加し、コミットがクリックされたときにshowSelectedCommit関数が呼び出されるようにしています。showSelectedCommit関数では、選択されたコミットの内容を取得してエディタに表示しています。

ただし、この方法では過去のコミットを表示している間にエディタで編集が行われると、新しいコミットが上書きされてしまう可能性があります。過去のコミットを表示している間は編集を無効にするか、適切なメッセージを表示してユーザーに注意喚起することを検討してください。

psmuler commented 1 year ago

mdFilenameは定義されていません

psmuler commented 1 year ago

すみません、mdFileNameが定義されていないことを見落としていました。以下のように修正して、選択されたブランチでの現在の作業ディレクトリの.mdファイルの内容を表示するようにしましょう。

renderer.js:

// 省略

// 選択されたコミットの内容を表示
async function showSelectedCommit(commitHash) {
  const folderPath = folderPathSpan.textContent;

  // フォルダ内の.mdファイルを探す
  const mdFiles = await new Promise((resolve, reject) => {
    fs.readdir(folderPath, (err, files) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(files.filter(file => file.endsWith('.md')));
    });
  });

  if (mdFiles.length === 0) {
    console.error('No markdown files found in the folder');
    return;
  }

  const mdFileName = mdFiles[0]; // 最初に見つかった.mdファイルを使用する

  const fileContent = await new Promise((resolve, reject) => {
    exec(`git -C "${folderPath}" show ${commitHash}:${mdFileName}`, (error, stdout, stderr) => {
      if (error && error.code !== 0) {
        console.error(`Error: ${error.message}`);
        reject(error);
        return;
      }
      if (stderr) {
        console.error(`Stderr: ${stderr}`);
      }
      resolve(stdout);
    });
  });

  const textEditor = document.getElementById('text-editor');
  textEditor.value = fileContent;
}

// 省略

// 選択されたブランチのコミット一覧を表示
async function showCommitList() {
  // 省略

  const commitLines = commitLogOutput.split('\n');
  commitLines.forEach((commitLine) => {
    const listItem = document.createElement('li');
    listItem.textContent = commitLine;
    listItem.addEventListener('click', () => {
      const commitHash = commitLine.split(' ')[0];
      showSelectedCommit(commitHash);
    });
    commitList.appendChild(listItem);
  });
}

// 省略

この修正では、showSelectedCommit関数内で選択されたフォルダ内の.mdファイルを探し、最初に見つかった.mdファイルをmdFileNameとして使用します。この方法で現在の作業ディレクトリの.mdファイルの内容を表示できます。

ただし、複数の.mdファイルが存在する場合は、どのファイルを表示するかをユーザーが選択できるようにすることを検討してください。