ctags.exeでtagsファイルを生成、タグジャンプを行うマクロ

提供: MeryWiki
ナビゲーションに移動 検索に移動

概要[編集]

ctags.exeを利用してtagsファイルを作成します。作成したtagsファイルを使用してタグジャンプします。

使い方[編集]

インストール[編集]

  1. TagJump.zipを解凍。
  2. tagJump.jsをMeryのMacrosフォルダなどに置く。
  3. マクロを登録し、F12キーなどに設定する。
  4. Mery.exeのあるフォルダにctags.exeを置く。

初回起動[編集]

  1. タグファイルが生成されていないので、どこに生成するか尋ねられる。
  2. 指定したディレクトリ以下のソースファイル全てが再帰的にタグファイルに登録される。

タグファイルの作成後[編集]

  1. カーソルを変数の名前などにあわせてマクロを起動する。
  2. 変数の定義元の候補の一覧が出てくる。
  3. メニューの項目を選ぶと、そのファイルにジャンプする。

ソースコード[編集]

var config = {
    // ポップアップ時にタグファイルの更新メニューを表示
    // true: 表示する  false: 表示しない
    CONFIRM_UPDATE: true,

    // ターゲットの定義行にジャンプしたときにずれを検出した場合の自動更新
    // true: 自動更新する  false: 自動更新しない
    AUTO_UPDATE: true,

    // 定義が一つしか見つからないときメニューを表示しないでジャンプする
    // true: 有効  false: 無効
    QUICK: true,

    // ctags.exe のパス
    // Mery.exe と同じフォルダに配置されているとして設定
    CTAGS_PATH: BuildPath(GetParentFolderName(window.Editor.FullName), "ctags.exe"),

    // ctags.exe のオプション
    CTAGS_OPTIONS: "--excmd=number --recurse=yes --fields=+S",

    // タグファイル名
    TAGS_NAME: "tags"
};

var tags;
function FileSystemObject(){
    return new ActiveXObject("Scripting.FileSystemObject");
}
function BuildPath(path, name){
    return FileSystemObject().BuildPath(path, name);
}
function GetAbsolutePathName(path){
    return FileSystemObject().GetAbsolutePathName(path);
}
function GetParentFolderName(path){
    return FileSystemObject().GetParentFolderName(path);
}
function FolderExists(folderspec){
    return FileSystemObject().FolderExists(folderspec);
}
function FileExists(filespec){
    return FileSystemObject().FileExists(filespec);
}
function OpenTextFile(filename){
    return FileSystemObject().OpenTextFile(filename, 1, false, -2);
}
function WScriptShell(cd){
    var shell = new ActiveXObject("WScript.Shell");
    shell.CurrentDirectory = cd;
    return shell;
}
function Run(cd, cmd, wait){
    return WScriptShell(cd).Run(cmd, 0, wait);
}

function OpenFile(file, encoding, flags){
    var i, j, e, d;
    file = GetAbsolutePathName(file);
    window.Editor.OpenFile(file, encoding, flags);
    for(i = 0; i < window.Editors.Count; i++){
        e = window.Editors.Item(i);
        for(j = 0; j < e.Documents.Count; j++){
            d = e.Documents.Item(j);
            if(d.FullName && d.FullName === file){
                return d;
            }
        }
    }
}
function jumpLine(doc, line){
    doc.Selection.SetActivePoint(mePosLogical, 1, line, false);
    return doc;
}
function activate(doc){
    doc.Activate();
    return doc;
}
function jump(file, line){
    return activate(jumpLine(OpenFile(file, meEncodingNone, meOpenAllowNewWindow), line));
}
function isModified(doc, keyword){
    return doc.GetLine(doc.Selection.GetActivePointY(mePosLogical), 0).indexOf(keyword) === -1;
}
function error(msg){
    window.Alert(msg);
    return window.Quit();
}
function askDir(dir){
    var ret = window.Prompt("Directory for creating tag file:", dir);
    if(!FolderExists(ret)){
        error(ret + " not found.");
    }
    return ret;
}
function confirmCreate(){
    return window.Confirm("Create a tag file?");
}
function runCtags(dir, wait){
    var cmd = [
        '"' + config.CTAGS_PATH + '"',
        "-f " + config.TAGS_NAME,
        config.CTAGS_OPTIONS].join(" ");
    var ret = Run(dir, cmd, wait);
    if(wait){
        if(ret !== 0){
            error(cmd + " faild.");
        }
        return ret;
    }
    return null;
}
function createTagfile(dir){
    var tag = BuildPath(dir, config.TAGS_NAME);
    runCtags(dir, true);
    if(!FileExists(tag)){
        error(tag + " not found.");
    }
    return tag;
}
function updateTag(){
    return runCtags(GetParentFolderName(tags), false);
}
function seekTagfile(cd){
    if(!FolderExists(cd)){
        return null;
    }
    if(FileExists(BuildPath(cd, config.TAGS_NAME))){
        return BuildPath(cd, config.TAGS_NAME);
    }
    return seekTagfile(GetParentFolderName(cd));
}
function getKeyword(str){
    return str.split("\t")[0];
}
function getPath(str){
    return str.split("\t")[1];
}
function getLine(str){
    return /\d+/.exec(str.split("\t")[2]);
}
function getType(str){
    return str.split("\t")[3]
}
function getOther(str){
    return str.split("\t").slice(4);
}
function scanTag(keyword, tagFile){
    var stream = OpenTextFile(tagFile);
    var result = [];
    var str;
    try{
        while(!stream.AtEndOfStream){
            str = stream.ReadLine();
            if(getKeyword(str) === keyword){
                result.push({
                    PATH: getPath(str),
                    LINE: getLine(str),
                    TYPE: getType(str),
                    OTHER: getOther(str)
                });
            }
        }
    }catch(err){
        error(err);
    }finally{
        stream.Close();
    }
    return result;
}
function toHotkey(n){
    return "&" + n.toString(36);
}
function Popup(list){
    var i;
    var menu = window.CreatePopupMenu();
    for(i = 0; i < list.length; i++){
        menu.Add([
            list[i].PATH,
            list[i].LINE,
            list[i].TYPE,
            list[i].OTHER,
            toHotkey(i)
        ].join(" "), i + 1, 0);
    }
    if(config.CONFIRM_UPDATE){
        if(i > 0){
            menu.Add("", 0, meMenuSeparator);
        }
        menu.Add("Update tag file.", i + 1, 0);
        list.push({
            UPDATE: true
        });
    }
    return list[menu.Track(0) - 1];
}
function isWhite(str){
    return str.replace(/\s+/, "").length === 0;
}
function tagJump(keyword){
    var ret, hits;
    tags = seekTagfile(window.Document.Path);
    if(!tags){
        if(confirmCreate()){
            tags = createTagfile(askDir(window.Document.Path));
        }else{
            return null;
        }
    }
    hits = scanTag(keyword, tags);
    if(hits.length === 0){
        window.Status = keyword + " not found.";
    }
    if(config.QUICK && hits.length === 1){
        ret = hits[0];
    }else{
        ret = Popup(hits);
    }
    if(ret){
        if(ret.UPDATE){
            return updateTag();
        }
        return jump(BuildPath(GetParentFolderName(tags), ret.PATH), ret.LINE);
    }
    return null;
}

var keyword, doc;
if(window.Document.Selection.IsEmpty){
    window.Document.Selection.SelectWord();
}
keyword = window.Document.Selection.Text;
if(!isWhite(keyword)){
    doc = tagJump(keyword);
    if(config.AUTO_UPDATE){
        if(doc){
            if(isModified(doc, keyword)){
                updateTag();
            }
        }
    }
}
window.Quit();
スポンサーリンク