Google Colab에서 코드 편집이 완료되는 Chrome 확장 프로그램을 개발했습니다.

결과



이렇게 되었다


배경



최근, 기계 학습 주위의 공부를 Google Colab 상에서 실시하게 되었다. 다만, 코드 편집 기능에 조금 불만을 느끼고 있었다. 그래서 최근에 학습한 Javascript와 Chrome 확장의 지식을 조합하여 코드 편집이 끝나는 Chrome 확장을 개발해보기로 했다.

출처



구현 방법



Google Colab(Jupyter Notebook에서도 OK)을 연 상태에서 Chrome Dev Tool의 Console에 다음 코드를 입력하면 각 셀에 CodeMirror라는 개체가 연결되어 있는지 확인할 수 있습니다.
const div = document.querySelector('div.CodeMirror')
div.CodeMirror

셀상에서 일어나는 이벤트는, 이 CodeMirror 객체에 의해 관리되고 있다. 즉, 이 객체의 프로퍼티를 재기록하는 것으로, 새로운 기능을 추가할 수 있다(기존의 기능을 오염하지 않도록 주의가 필요). 개발시는 공식 문서와 GitHub 상의 소스 코드를 참고로 했다.
  • CodeMirror: User Manual
  • CodeMirror: GitHub

  • 여담이지만 Jupyter Notebook이나 Kaggle의 Kernel에서도 CodeMirror가 사용되고 있다.

    구현한 기능


  • 스 니펫
  • 스 니펫 팁
  • Vim ("구현"이 아니라, "Enable"했다고 하는 표현이 옳을지도)

  • 스니펫



    CodeMirror 객체가 프리미티브한 조작(커서 이동, 셀내 캐릭터 라인의 취득·치환·삭제 등)을 실시하는 함수를 가지고 있으므로, 이것들을 조합해 스니펫 기능을 실현하고 있다.
    const snippets = {
      'inp'     : 'import numpy as np\n',
      'iplt'    : 'import matplotlib.pyplot as plt\n',
      'ipd'     : 'import pandas as pd\n',
      'isb'     : 'import seaborn as sns\n',
      'itf'     : 'import tensorflow as tf\n',
      'pdrc'    : 'pd.read_csv()'
    };
    
    const expandSnippetOrIndent = cm => {
      // cm: CodeMirror object
      const cursor = cm.getCursor();  // cursor position
      const cursorLeft = cm.getRange({line: cursor.line, ch: 0}, cursor); // カーソルより左側の文字列
      const match = cursorLeft.match(/[^a-zA-Z0-9_]?([a-zA-Z0-9_]+)$/);
      if (!match) {
        tabDefaultFunc(cm);
        return
      }
    
      const prefix = match[1];
      const head = {line: cursor.line, ch: cursor.ch - prefix.length};
    
      // マッチあり
      if (prefix in snippets) {
        const body = snippets[prefix];
        cm.replaceRange(body, head, cursor);
        const match = body.match(/\)+$/);
        if (match) cm.moveH(-match[0].length, 'char');
      } else {
        tabDefaultFunc(cm);
      }
    
      // TODO:展開候補が複数個があった場合には、ヒントを表示する機能を実装
    }
    
    // Tab にスニペット展開機能を割り当て
    cell.CodeMirror.options.extraKeys['Tab'] = expandSnippetOrIndent;
    

    스니펫 팁



    하고 있는 것은 스니펫 기능과 거의 같다. 힌트 표시중에, 유저로부터 입력이 있으면, 힌트가 자동 갱신되도록 하고 있다.
    const showSnippetHint = cm => {
      // TODO: expandSnippetOrIndent と被っている処理は関数化
      const cursor = cm.getCursor();
      const cursorLeft = cm.getRange({line: cursor.line, ch: 0}, cursor);
      const match = cursorLeft.match(/[^a-zA-Z0-9_]?([a-zA-Z0-9_]+)$/);
      const prefix = match ? match[1] : '';
      const head = {line: cursor.line, ch: cursor.ch - prefix.length};
      const matchedPrefixes = Object.keys(snippets).filter(k => k.indexOf(prefix) > -1);
      matchedPrefixes.sort();
    
      const hintList = matchedPrefixes.map(key => {
        const displayText = snippets[key].replace('\n', '; ');
        const displayTextTrunc = displayText.length > 40 ? displayText.slice(0, 40) + '...' : displayText
        return {
          text: snippets[key], // 挿入される文字列
          displayText: `${key.padEnd(7, ' ')}: ${displayTextTrunc}`  // ヒントに表示される文字列
        }
      });
    
      const hintFunc = () => {
        return {
          list: hintList,
          from: head,
          to: cursor
        }
      }
    
      // ヒントを表示
      cm.showHint({
        hint: hintFunc,
      });
    }
    
    // ヒント表示中に、ユーザーから入力があったら、ヒントを更新する
    const conCursorActivity = cm => {
      if (cm.state.completionActive) {
        showSnippetHint(cm);
      }
    }
    
    // Ctrl-h にヒント表示機能を割り当て
    cell.CodeMirror.options.extraKeys['Ctrl-H'] = showSnippetHint;
    cell.CodeMirror.on('cursorActivity', conCursorActivity);
    

    Vim



    선구자가 있었기 때문에 그것을 참고했다. Chrome 확장 파일 구성도이 리포지토리와 유사합니다.
  • Autovim - Chrome 웹 스토어
  • thomcom/autovim
  • const enableVim = cell => {
      cell.CodeMirror.setOption('vimMode', true);
      cell.CodeMirror.options.keyMap = 'vim';
      cell.CodeMirror.options.showCursorWhenSelecting = 'vim';
    };
    

    요약



    개발한 Chrome 확장 덕분에 Google Colab에서 작업 효율이 크게 향상되었습니다. 본 기사에서 소개하고 있는 방법을 이용하여 Jupyter Notebook도 해킹해 볼 예정. 끝까지 읽어 주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기