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

提供: MeryWiki
ナビゲーションに移動 検索に移動
編集の要約なし
r5に更新
3行目: 3行目:
項目を選ぶとその単語が挿入されます。
項目を選ぶとその単語が挿入されます。


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


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


*インストール
*インストール


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


26行目: 27行目:


<source lang="javascript">
<source lang="javascript">
// タグファイルから補完.js r4
// タグファイルから補完.js r5
// http://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


/** 設定 **/
/** 設定 **/
36行目: 38行目:
// 相対パスの場合は以下のディレクトリから順に探します
// 相対パスの場合は以下のディレクトリから順に探します
//  ・mery本体と同じディレクトリ
//  ・mery本体と同じディレクトリ
//  ・スクリプトと同じディレクトリ
//  ・何もなし(PATHにあることを期待)
var CTAGS = "ctags.exe"
var CTAGS = "ctags.exe"


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


// 単語の途中で補完する際の挙動
// 単語の途中で補完する際の挙動
47行目: 47行目:
// falseにするとカーソルより左から補完します
// falseにするとカーソルより左から補完します
var WHOLE_WORD = false
var WHOLE_WORD = false
// 単語の検索の挙動
// trueにすると検索時大文字と小文字を区別しません
// falseにすると区別します
var CASE_INSENSITIVE = true


/** 本体 **/
/** 本体 **/
60行目: 65行目:
   var tags = makeTags(ctags)
   var tags = makeTags(ctags)
   var query = getQueryWord()
   var query = getQueryWord()
   var words = tags.lookup(query)
   var result = tags.lookup(query)
   var word = chooseWord(words, tags)
   var word = chooseWord(result)
    
    
   writeWord(word)
   if ("string" === typeof word)
  {
    writeWord(word)
  }
  else if (word === false)
  {
    tags.update()
  }
  else
  {
    jumpToDefinition(word)
  }
}
}


74行目: 90行目:
     var ctags = findCtags()
     var ctags = findCtags()
      
      
     if (!ctags)
     try
    {
      Shell.CurrentDirectory = dir
      window.Status = 'タグファイルを作成しています お待ちください'
      Shell.Run(ctags + " -o " + TAGS + " " + CTAGS_FLAGS, 0, true)
    }
    catch (e)
     {
     {
      throw new Error("ctags.exeが見つかりません")
     }
     }
      
      
    window.Status = 'タグファイルを作成しています お待ちください'
    Shell.CurrentDirectory = dir
    Shell.Run(ctags + " -f " + TAGS + " " + CTAGS_FLAGS, 0, true)
     window.Status = 'タグファイルを作成しました'
     window.Status = 'タグファイルを作成しました'
     return FileSystem.BuildPath(dir, TAGS)
     return FileSystem.BuildPath(dir, TAGS)
88行目: 106行目:
   function findCtags()
   function findCtags()
   {
   {
      return findCtagsFrom(window.ScriptFullName) ||
    if (FileSystem.FileExists(CTAGS))
        findCtagsFrom(editor.FullName) ||
    {
        findCtagsFrom("")
      return '"' + CTAGS + '"'
  }
    }
 
   
  function findCtagsFrom(hint)
     var dir = FileSystem.GetParentFolderName(editor.FullName)
  {
     var dir = FileSystem.GetParentFolderName(hint)
     var ctags = FileSystem.BuildPath(dir, CTAGS)
     var ctags = FileSystem.BuildPath(dir, CTAGS)
      
      
     if (FileSystem.FileExists(ctags))
     if (FileSystem.FileExists(ctags))
     {
     {
       return hint == "" ? CTAGS : '"' + FileSystem.GetAbsolutePathName(ctags) + '"'
       return '"' + ctags + '"'
     }
     }
   
    throw new Error("ctags.exeが見つかりませんでした\nMery本体のディレクトリに置くか、絶対パスを設定してください")
   }
   }
}
}
107行目: 125行目:
function makeTags(ctags)
function makeTags(ctags)
{
{
  var fullname = findTagfile()
   var text = loadTagfile()
   var text = loadTagfile()
    
    
   return { lookup: lookup, update: update }
   return { lookup: lookup, update: update }
 
  function getPath()
  {
    return FileSystem.GetParentFolderName(fullname)
  }
    
    
   function lookup(word)
   function lookup(word)
   {
   {
     var re = new RegExp("^" + reEscape(word) + "[^\\t]*", "igm")
    if (/^\s*$/.test(word)) { return }
     return text.match(re)
   
    word = reEscape(word)
   
     var re = new RegExp("^" + word + ".*$", CASE_INSENSITIVE ? "igm" : "gm")
    var lines = text.match(re)
   
    if (!lines) { return [] }
   
    return calcWords().concat(calcDefinitions(word))
   
    function calcWords()
    {
      if (lines.length === 1) { return [] }
     
      var words = new Array(lines.length)
     
      words[0] = lines[0].substring(0, lines[0].indexOf("\t"))
     
      var n = 0
      for (var i = 1; i < lines.length; i++)
      {
        var word = lines[i].substring(0, lines[i].indexOf("\t"))
       
        if (words[n] !== word)
        {
          n++
          words[n] = word
        }
      }
     
      words.length = n + 1
 
      words.sort(function(a, b)
      {
        return a.localeCompare(b)
      })
     
      return words
    }
   
    function calcDefinitions(word)
    {
      var defs = new Array(lines.length)
      var n = 0
     
      for (var i = 0; i < lines.length; i++)
      {
        var tab1 = lines[i].indexOf("\t")
       
        if (tab1 !== word.length) { continue }
       
        var tab2 = lines[i].indexOf("\t", tab1 + 1)
        var endnum = lines[i].indexOf(";", tab2 + 1)
        var sig = lines[i].indexOf("signature:", endnum + 1)
       
        var name = lines[i].substring(0, tab1)
        var filename = lines[i].substring(tab1 + 1, tab2)
        var line = lines[i].substring(tab2 + 1, endnum)
        var signature = sig < 0 ? "" : lines[i].substring(sig + "signature:".length)
       
        var fullname = FileSystem.BuildPath(getPath(), filename)
       
        defs[n] = [fullname, line, 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)
   function reEscape(w)
   {
   {
     return /^\s*$/.test(w) ? "\\w" : w.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
     return w.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
   }
   }
    
    
   function loadTagfile()
   function loadTagfile()
   {
   {
    var tagfile = findTagfile()
     if (!fullname)
   
     if (!tagfile)
     {
     {
       tagfile = makeTagfile()
       fullname = makeTagfile()
     }
     }
      
      
     return slurp(tagfile)
     return slurp(fullname)
   }
   }
    
    
160行目: 257行目:
   function update()
   function update()
   {
   {
    var file = findTagfile()
     return ctags.run(getPath())
    if (!file) { throw new Error("タグファイルが見つかりません") }
    var dir = FileSystem.GetParentFolderName(file)
     return ctags.run(dir)
   }
   }
    
    
205行目: 299行目:
}
}


function chooseWord(words, tags)
function chooseWord(words)
{
{
  if (words && words.length === 1 && typeof words[0] === "string")
  {
    return words[0]
  }
   var menu = makeMenu()
   var menu = makeMenu()
   var i = menu.Track(0)
   var i = menu.Track(0)
    
    
   if (i == -1)
   return i === -1 ? false : words && words[i - 1]
  {
    tags.update()
  }
 
  return words && words[i - 1]
    
    
   function makeMenu()
   function makeMenu()
   {
   {
     var menu = window.CreatePopupMenu()
     var menu = window.CreatePopupMenu()
   
     initMenu(menu)
     initMenu(menu)
     menu.Add("タグファイルを更新する", -1)
     menu.Add("タグファイルを更新する", -1)
   
     return menu
     return menu
   }
   }
230行目: 322行目:
   {
   {
     if (!words || words.length == 0) { return }
     if (!words || words.length == 0) { return }
   
    uniq(words)
   
    words.sort(function(a, b)
    {
      return a.localeCompare(b)
    })
      
      
     var n = 0
     var n = 0
253行目: 338行目:
     menu.Add(undefined, 0, meMenuSeparator)
     menu.Add(undefined, 0, meMenuSeparator)
   }
   }
}
function jumpToDefinition(def)
{
  if (!def) { return }
  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()
}
}


261行目: 358行目:
     document.selection.Text = word
     document.selection.Text = word
   }
   }
}
function uniq(array)
{
  var n = 0
  for (var i = 1; i < array.length; i++)
  {
    if (array[n] !== array[i])
    {
      n++
      array[n] = array[i]
    }
  }
  array.length = n + 1
}
}
</source>
</source>

2012年8月1日 (水) 15:40時点における版

ctagsの生成したtagsファイルから検索して単語のメニューを出します。

項目を選ぶとその単語が挿入されます。

単語が完全な形の場合、タグジャンプを行います。

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

  • インストール
  1. 下のスクリプトをコピーして、MeryのMy Macroフォルダなどに保存
  2. マクロを登録し、Ctrl+→やCtrl+Shift+SpaceやCtrl+Qキーなどにお好みで設定する
  3. Mery.exeのあるフォルダに[ctags.exe]を置く
  • 初回起動
  1. タグファイルが生成されていないので、どこに生成するか尋ねられる
  2. 指定したディレクトリ以下のソースファイル全てが再帰的にタグファイルに登録される
  3. 「タグファイルの作成が完了しました」とステータスバーに出るまで待つ
  • タグファイルの作成後
  1. 補完したい単語の右にカーソルを置き、マクロを起動する
  2. メニューの項目を選ぶと、その単語が挿入される
  3. メニューの最後の「タグファイルを更新する」を選ぶと、タグファイルを作り直すことができる


// タグファイルから補完.js r5
// http://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

/** 設定 **/

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

// ctags.exeの名前
// 相対パスの場合は以下のディレクトリから順に探します
//   ・mery本体と同じディレクトリ
var CTAGS = "ctags.exe"

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

// 単語の途中で補完する際の挙動
// trueにするとカーソルの置かれた単語全体から補完
// falseにするとカーソルより左から補完します
var WHOLE_WORD = false

// 単語の検索の挙動
// trueにすると検索時大文字と小文字を区別しません
// falseにすると区別します
var CASE_INSENSITIVE = true

/** 本体 **/

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

main()

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

function makeCtags()
{
  return { run: run }

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

function makeTags(ctags)
{
  var fullname = findTagfile()
  var text = loadTagfile()
  
  return { lookup: lookup, update: update }
  
  function getPath()
  {
    return FileSystem.GetParentFolderName(fullname)
  }
  
  function lookup(word)
  {
    if (/^\s*$/.test(word)) { return }
    
    word = reEscape(word)
    
    var re = new RegExp("^" + word + ".*$", CASE_INSENSITIVE ? "igm" : "gm")
    var lines = text.match(re)
    
    if (!lines) { return [] }
    
    return calcWords().concat(calcDefinitions(word))
    
    function calcWords()
    {
      if (lines.length === 1) { return [] }
      
      var words = new Array(lines.length)
      
      words[0] = lines[0].substring(0, lines[0].indexOf("\t"))
      
      var n = 0
      for (var i = 1; i < lines.length; i++)
      {
        var word = lines[i].substring(0, lines[i].indexOf("\t"))
        
        if (words[n] !== word)
        {
          n++
          words[n] = word
        }
      }
      
      words.length = n + 1

      words.sort(function(a, b)
      {
        return a.localeCompare(b)
      })
      
      return words
    }
    
    function calcDefinitions(word)
    {
      var defs = new Array(lines.length)
      var n = 0
      
      for (var i = 0; i < lines.length; i++)
      {
        var tab1 = lines[i].indexOf("\t")
        
        if (tab1 !== word.length) { continue }
        
        var tab2 = lines[i].indexOf("\t", tab1 + 1)
        var endnum = lines[i].indexOf(";", tab2 + 1)
        var sig = lines[i].indexOf("signature:", endnum + 1)
        
        var name = lines[i].substring(0, tab1)
        var filename = lines[i].substring(tab1 + 1, tab2)
        var line = lines[i].substring(tab2 + 1, endnum)
        var signature = sig < 0 ? "" : lines[i].substring(sig + "signature:".length)
        
        var fullname = FileSystem.BuildPath(getPath(), filename)
        
        defs[n] = [fullname, line, 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 = window.Prompt("タグファイル作成フォルダ", document.Path)
    
    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()
{
  var s = document.selection
  
  if (!s.IsEmpty) { return s.Text }
  
  if (WHOLE_WORD)
  {
    s.SelectWord()
    
    // hoge|(); → hogeFuga|(); のように補完 
    if (s.Text.length == 1 && !/\w/.test(s.Text))
    {
      s.Collapse(meCollapseStart)
      s.WordLeft(true)
    }
  }
  else
  {
    s.WordLeft(true)
  }
  
  if (/\A\s+\z/.test(s.Text))
  {
    s.Collapse(meCollapseEnd)
  }
  
  return s.Text
}

function chooseWord(words)
{
  if (words && words.length === 1 && typeof words[0] === "string")
  {
    return words[0]
  }

  var menu = makeMenu()
  var i = menu.Track(0)
  
  return i === -1 ? false : words && words[i - 1]
  
  function makeMenu()
  {
    var menu = window.CreatePopupMenu()
    initMenu(menu)
    menu.Add("タグファイルを更新する", -1)
    return menu
  }
  
  function initMenu(menu)
  {
    if (!words || words.length == 0) { return }
    
    var n = 0
    function addHotkey(word)
    {
      if (n > 34) { return word }
      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 jumpToDefinition(def)
{
  if (!def) { return }
  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 writeWord(word)
{
  if (word)
  {
    document.selection.Text = word
  }
}
スポンサーリンク