「タグファイルから補完」の版間の差分

編集の要約なし
MSY-07 (トーク | 投稿記録)
変数にvarの宣言を追加
 
(3人の利用者による、間の25版が非表示)
3行目: 3行目:
項目を選ぶとその単語が挿入されます。
項目を選ぶとその単語が挿入されます。


kazyさんの[[Ctags.exeでtagsファイルを生成、タグジャンプを行うマクロ]]とあわせてお使いください。
単語が完全な形の場合、タグジャンプを行います。


kazyさんの[[ctags.exeでtagsファイルを生成、タグジャンプを行うマクロ]]とあわせてお使いください(単独で使ってもタグジャンプ機能はあります)。


*インストール
== インストール ==
#下のスクリプトをコピーして、MeryのMacrosフォルダなどに保存
#Mery.exeのあるフォルダに[https://web.archive.org/web/20241217153248/https://hp.vector.co.jp/authors/VA025040/ctags/ ctags.exe]を置く
#マクロを登録し、Ctrl+→やCtrl+Shift+SpaceやCtrl+Qキーなどにお好みで設定する


#下のスクリプトをコピーして、MeryのMy Macroフォルダなどに保存
== 使い方 ==
#マクロを登録し、Ctrl+Shift+SpaceやCtrl+Qキーなどに設定する
#補完したい単語にカーソルを置き、マクロを起動する
#Mery.exeのあるフォルダに[[http://hp.vector.co.jp/authors/VA025040/ctags/ ctags.exe]]を置く
#初回起動の場合、タグファイルの作成を待つ
#すでに単語が完全な場合、メニューを選ぶとその定義にジャンプする
#メニューの項目を選ぶと、その単語が挿入される
#メニューの最後の「タグファイルを更新する」を選ぶと、タグファイルを作り直すことができる
 
== カスタマイズ ==
変数を変えることで、挙動がいくらかカスタマイズできます。
 
{| class="wikitable"
|-
! 名前 !! 初期設定 !! 効果
|-
| TAGS || ".merytags" || タグファイルの名前。
|-
| CTAGS || "ctags.exe" || ctags.exeの名前。
 
相対パスの場合はmery本体のあるディレクトリのものを使います。
 
"c:\\bin\\ctags.exe" のように絶対パスも指定できます。
|-
| CTAGS_FLAGS || "--excmd=number --recurse --fields=+S" || ctagsに与える引数。


*初回起動
初期設定の意味は左から
* 位置を行番号で記憶 (タグジャンプに必要)
* フォルダを再帰的に処理
* 関数の引数を記憶
|-
| WHOLE_WORD || true || 単語の途中で補完する際の挙動。


#タグファイルが生成されていないので、どこに生成するか尋ねられる
trueにするとカーソルの置かれた単語全体から補完。
#指定したディレクトリ以下のソースファイル全てが再帰的にタグファイルに登録される
#「タグファイルの作成が完了しました」とステータスバーに出るまで待つ


*タグファイルの作成後
falseにするとカーソルより左から補完します。
|-
| CASE_INSENSITIVE || true || 単語の検索の挙動。


#補完したい単語の右にカーソルを置き、マクロを起動する
trueにすると検索時大文字と小文字を区別しません。
#メニューの項目を選ぶと、その単語が挿入される
 
#メニューの最後の「タグファイルを更新する」を選ぶと、タグファイルを作り直すことができる
falseにすると区別します。
|-
| DEFAULT_TAGS_DIR || "." || タグを作るフォルダ。
 
"." にすると、編集中のファイルのあるフォルダでタグを作ります。
 
".." などにすると、編集中のファイルの一つ上のフォルダからタグを作ります。
|-
| CONFIRM_CREATE_TAGS || true || タグファイルがない場合の挙動。
 
trueにするとタグを作成するフォルダを尋ねます。
 
falseにするとフォルダを尋ねずに無言で作成を始めます。
|}
 
== 変更履歴 ==
* r7 (2012-10-14)
** 補完候補の単語が1つだけのときうまく補完されないのを修正
* r6 (2012-08-23)
** 確認せずにctagsを起動するオプション
* r5 (2012-08-01)
** C言語の関数で引数を表示、今開いてるファイル中の単語からも補完、文字の大小を区別しないオプション
* r4 (2012-06-22)
** ホットキー
* r3
** 単独でctags呼び出し
* r2
** 失念
* r1
** 失念


== ソースコード ==
<syntaxhighlight lang="javascript" copy>
// タグファイルから補完.js r7 2012-10-24
// https://www.haijin-boys.com/wiki/%E3%82%BF%E3%82%B0%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8B%E3%82%89%E8%A3%9C%E5%AE%8C


<source lang="javascript">
// https://hp.vector.co.jp/authors/VA025040/ctags/ の 日本語版ctags 5.8J2 で動作確認しています
// タグファイルから補完.js r4


/** 設定 **/
/** 設定 **/


// タグファイルの名前
// タグファイルの名前
var TAGS = "tags"
var TAGS = ".merytags";


// ctags.exeの名前
// ctags.exeの名前
// 相対パスの場合は以下のディレクトリから順に探します
// 相対パスの場合はmery本体のあるディレクトリのものを使います
//   ・mery本体と同じディレクトリ
// "c:\\bin\\ctags.exe" のように絶対パスも指定できます
//  ・スクリプトと同じディレクトリ
var CTAGS = "ctags.exe";
//  ・何もなし(PATHにあることを期待)
var CTAGS = "ctags.exe"


// ctagsに与える引数 (初期設定:位置を行番号で記憶(tagJump.js互換)、フォルダを再帰的に処理)
// ctagsに与える引数 (初期設定:位置を行番号で記憶(必須)、フォルダを再帰的に処理、関数の引数を記憶)
var CTAGS_FLAGS = "--excmd=number --recurse"
var CTAGS_FLAGS = "--excmd=number --recurse --fields=+S";


// 単語の途中で補完する際の挙動
// 単語の途中で補完する際の挙動
// trueにするとカーソルの置かれた単語全体から補完
// trueにするとカーソルの置かれた単語全体から補完
// falseにするとカーソルより左から補完します
// falseにするとカーソルより左から補完します
var WHOLE_WORD = false
var WHOLE_WORD = true;
 
// 単語の検索の挙動
// trueにすると検索時大文字と小文字を区別しません
// falseにすると区別します
var CASE_INSENSITIVE = true;
 
// タグを作るフォルダ
// "." にすると、編集中のファイルのあるフォルダでタグを作ります
// ".." などにすると、編集中のファイルの一つ上のフォルダからタグを作ります
var DEFAULT_TAGS_DIR = ".";
 
// タグファイルがない場合の挙動
// trueにするとタグを作成するフォルダを尋ねます
// falseにするとフォルダを尋ねずに無言で作成を始めます
var CONFIRM_CREATE_TAGS = false;


/** 本体 **/
/** 本体 **/


var FileSystem = new ActiveXObject("Scripting.FileSystemObject")
var FileSystem = new ActiveXObject("Scripting.FileSystemObject");
var Shell = new ActiveXObject("WScript.Shell")
var Shell = new ActiveXObject("WScript.Shell");
 
main();


main()
function main() {
var ctags = makeCtags();
var tags = makeTags(ctags);
var query = getQueryWord();
var result = tags.lookup(query);
var word = chooseWord(result);


function main()
if ("string" === typeof word) {
{
writeWord(word);
  var ctags = makeCtags()
} else if (word === false) {
  var tags = makeTags(ctags)
tags.update();
  var query = getQueryWord()
} else {
  var words = tags.lookup(query)
jumpToDefinition(word);
  var word = chooseWord(words, tags)
}
 
  writeWord(word)
}
}


function makeCtags()
function makeCtags() {
{
return {
  return { run: run }
run: run
};


  function run(dir)
function run(dir) {
  {
var ctags = findCtags();
    var ctags = findCtags()
 
   
try {
    if (!ctags)
Shell.CurrentDirectory = dir;
    {
window.Status = 'タグファイルを作成しています お待ちください';
      throw new Error("ctags.exeが見つかりません")
Shell.Run('"' + ctags + '" -o ' + TAGS + " " + CTAGS_FLAGS, 0, true);
    }
} catch (e) {}
   
 
    window.Status = 'タグファイルを作成しています お待ちください'
window.Status = 'タグファイルを作成しました';
    Shell.CurrentDirectory = dir
return FileSystem.BuildPath(dir, TAGS);
    Shell.Run(ctags + " -f " + TAGS + " " + CTAGS_FLAGS, 0, true)
}
    window.Status = 'タグファイルを作成しました'
 
    return FileSystem.BuildPath(dir, TAGS)
function findCtags() {
  }
if (FileSystem.FileExists(CTAGS)) {
 
return CTAGS;
  function findCtags()
}
  {
 
      return findCtagsFrom(window.ScriptFullName) ||
var dir = FileSystem.GetParentFolderName(editor.FullName);
        findCtagsFrom(editor.FullName) ||
var ctags = FileSystem.BuildPath(dir, CTAGS);
        findCtagsFrom("")
 
  }
if (FileSystem.FileExists(ctags)) {
 
return ctags;
  function findCtagsFrom(hint)
}
  {
 
    var dir = FileSystem.GetParentFolderName(hint)
throw new Error("ctags.exeが見つかりませんでした\nMery本体のディレクトリに置くか、絶対パスを設定してください");
    var ctags = FileSystem.BuildPath(dir, CTAGS)
}
   
    if (FileSystem.FileExists(ctags))
    {
      return hint == "" ? CTAGS : '"' + FileSystem.GetAbsolutePathName(ctags) + '"'
    }
  }
}
}


function makeTags(ctags)
function makeTags(ctags) {
{
var fullname = findTagfile();
  var text = loadTagfile()
var text = loadTagfile();
 
 
  return { lookup: lookup, update: update }
return {
 
lookup: lookup,
  function lookup(word)
update: update
  {
};
    var re = new RegExp("^" + reEscape(word) + "[^\\t]*", "igm")
 
    return text.match(re)
function getPath() {
  }
return FileSystem.GetParentFolderName(fullname);
 
}
  function reEscape(w)
 
  {
function lookup(word) {
    return /^\s*$/.test(w) ? "\\w" : w.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
if (/^\s*$/.test(word)) {
  }
return;
 
}
  function loadTagfile()
 
  {
word = reEscape(word);
    var tagfile = findTagfile()
 
   
var opts = CASE_INSENSITIVE ? "igm" : "gm";
    if (!tagfile)
var re = new RegExp("^" + word + ".*$", opts);
    {
var lines = text.match(re);
      tagfile = makeTagfile()
 
    }
if (!lines) {
   
return [];
    return slurp(tagfile)
}
  }
 
 
return calcWords().concat(calcDefinitions(word));
  function findTagfile()
 
  {
function calcWords() {
    var dir = document.Path
var words = new Array(lines.length);
   
 
    while (FileSystem.FolderExists(dir))
words[0] = lines[0].substring(0, lines[0].indexOf("\t"));
    {
 
      var file = FileSystem.BuildPath(dir, TAGS)
if (words.length === 1) {
      if (FileSystem.FileExists(file)) { return file }
return words[0] === word ? [] : words;
      dir = FileSystem.GetParentFolderName(dir)
}
    }
 
  }
var n = 0;
 
for (var i = 1; i < lines.length; i++) {
  function makeTagfile()
var w = lines[i].substring(0, lines[i].indexOf("\t"));
  {
 
    var dir = window.Prompt("タグファイル作成フォルダ", document.Path)
if (words[n] !== w) {
   
n++;
    if (!dir)
words[n] = w;
    {
}
      window.Quit()
}
    }
 
   
words.length = n + 1;
    return ctags.run(dir)
 
  }
words.sort(function(a, b) {
 
return a.localeCompare(b);
  function update()
});
  {
 
    var file = findTagfile()
return words;
    if (!file) { throw new Error("タグファイルが見つかりません") }
}
    var dir = FileSystem.GetParentFolderName(file)
 
    return ctags.run(dir)
function calcDefinitions(word) {
  }
var defs = new Array(lines.length);
 
var n = 0;
  function slurp(filename)
 
  {
for (var i = 0; i < lines.length; i++) {
    var f = FileSystem.OpenTextFile(filename)
var line = lines[i];
    var text = f.ReadAll()
var tab1 = line.indexOf("\t");
    f.Close()
 
    return text
if (tab1 !== word.length) {
  }
continue;
}
 
var tab2 = line.indexOf("\t", tab1 + 1);
var endnum = line.indexOf(";", tab2 + 1);
var sig = line.indexOf("signature:", endnum + 1);
 
var name = line.substring(0, tab1);
var filename = line.substring(tab1 + 1, tab2);
var linenum = line.substring(tab2 + 1, endnum);
var signature = sig < 0 ? "" : line.substring(sig + "signature:".length);
 
var fullname = FileSystem.BuildPath(getPath(), filename);
 
defs[n] = [fullname, linenum, filename, name + signature];
defs[n].toString = joinme;
n++;
}
 
defs.length = n;
 
return defs;
}
 
function joinme() {
return this[3] + " (" + this[2] + " : " + this[1] + ")";
}
}
 
function reEscape(w) {
return w.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
 
function loadTagfile() {
if (!fullname) {
fullname = makeTagfile();
}
 
return slurp(fullname);
}
 
function findTagfile() {
var dir = document.Path;
 
while (FileSystem.FolderExists(dir)) {
var file = FileSystem.BuildPath(dir, TAGS);
if (FileSystem.FileExists(file)) {
return file;
}
dir = FileSystem.GetParentFolderName(dir);
}
}
 
function makeTagfile() {
var dir = FileSystem.BuildPath(document.Path, DEFAULT_TAGS_DIR);
 
if (CONFIRM_CREATE_TAGS) {
dir = window.Prompt("タグファイル作成フォルダ", dir);
}
 
if (!dir) {
window.Quit();
}
 
return ctags.run(dir);
}
 
function update() {
return ctags.run(getPath());
}
 
function slurp(filename) {
var f = FileSystem.OpenTextFile(filename);
var text = f.ReadAll();
f.Close();
return text;
}
}
}


function getQueryWord()
function getQueryWord() {
{
var s = document.selection;
  var s = document.selection
 
 
if (!s.IsEmpty) {
  if (!s.IsEmpty) { return s.Text }
return s.Text;
 
}
  if (WHOLE_WORD)
 
  {
if (WHOLE_WORD) {
    s.SelectWord()
s.SelectWord();
   
 
    // hoge|(); → hogeFuga|(); のように補完  
// hoge|(); → hogeFuga|(); のように補完  
    if (s.Text.length == 1 && !/\w/.test(s.Text))
if (s.Text.length == 1 && !/\w/.test(s.Text)) {
    {
s.Collapse(meCollapseStart);
      s.Collapse(meCollapseStart)
s.WordLeft(true);
      s.WordLeft(true)
}
    }
} else {
  }
s.WordLeft(true);
  else
}
  {
 
    s.WordLeft(true)
if (/\A\s+\z/.test(s.Text)) {
  }
s.Collapse(meCollapseEnd);
 
}
  if (/\A\s+\z/.test(s.Text))
 
  {
return s.Text;
    s.Collapse(meCollapseEnd)
  }
 
  return s.Text
}
}


function chooseWord(words, tags)
function chooseWord(words) {
{
if (words && words.length === 1 && typeof words[0] === "string") {
  var menu = makeMenu()
return words[0];
  var i = menu.Track(0)
}
 
 
  if (i == -1)
var menu = makeMenu();
  {
var i = menu.Track(0);
    tags.update()
 
  }
return i === -1 ? false : words && words[i - 1];
 
 
  return words && words[i - 1]
function makeMenu() {
 
var menu = window.CreatePopupMenu();
  function makeMenu()
initMenu(menu);
  {
menu.Add("タグファイルを更新する", -1);
    var menu = window.CreatePopupMenu()
return menu;
   
}
    initMenu(menu)
 
    menu.Add("タグファイルを更新する", -1)
function initMenu(menu) {
   
if (!words || words.length == 0) {
    return menu
return;
  }
}
 
 
  function initMenu(menu)
var n = 0;
  {
 
    if (!words || words.length == 0) { return }
function addHotkey(word) {
   
if (n > 34) {
    uniq(words)
return word;
   
}
    words.sort(function(a, b)
n++;
    {
return "&" + n.toString(36) + ". " + word;
      return a.localeCompare(b)
}
    })
 
   
for (var i = 0; i < words.length; i++) {
    var n = 0
menu.Add(addHotkey(words[i]), i + 1);
    function addHotkey(word)
}
    {
 
      if (n > 34) { return word }
menu.Add(undefined, 0, meMenuSeparator);
      n++
}
      return "&" + n.toString(36) + ". " + word
    }
   
    for (var i = 0; i < words.length; i++)
    {
      menu.Add(addHotkey(words[i]), i + 1)
    }
   
    menu.Add(undefined, 0, meMenuSeparator)
  }
}
}


function writeWord(word)
function jumpToDefinition(def) {
{
if (!def) {
  if (word)
return;
  {
}
    document.selection.Text = word
window.Status = def + "にジャンプします";
  }
var file = def[0];
var line = def[1];
 
editor.OpenFile(file, 0, meOpenAllowNewWindow);
editor.ActiveDocument.selection.SetActivePoint(mePosLogical, 1, line, false);
editor.ActiveDocument.Activate();
}
}


function uniq(array)
function writeWord(word) {
{
if (word) {
  var n = 0
document.selection.Text = word;
  for (var i = 1; i < array.length; i++)
}
  {
    if (array[n] !== array[i])
    {
      n++
      array[n] = array[i]
    }
  }
  array.length = n + 1
}
}
</source>
</syntaxhighlight>
スポンサーリンク