いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

VB用のタグファイル作成ツール

本記事のコメントにて、そとさんがコメントされているように
ctagsはVBに対応しているとの事です。そとさんありがとうございます! m(_ _)m


以下、そとさんのコメントです。


>私と同じくここにたどり着いた人のために。
>ctagsはVBに対応しています。
>(そのへんの説明が少なくて気づきにくいですが)
>
>ctags -f xxx.tags --excmd=number -R --languages=Basic --langmap=Basic:.frm.bas.cls --Basic-types=-l -L srcList.txt


vimでソースを見たり書いたりすることが多いのですが、その際にタグファイルを
作成してジャンプしたりすることがよくあります。


有名なのがctagsですね。


で、このツールはVBに対応していないのでVBのソースを見たりするときに
面倒なので、ヘボイですが作ってみました。
(対応しているとの事です。)


データをヒットさせる際の正規表現は以下を参考にさせていただきました。
感謝m(_ _)m
http://homepage1.nifty.com/markey/memo/200406.html#061601


以下、ソースです。
分けて載せるのが面倒なので一ファイルとして貼り付けてます。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace VBTags{

    /// <summary>
    /// アプリケーションメインエントリポイントを表します。
    /// </summary>
    public class EntryPoint {

        /// <summary>
        /// エントリポイントメソッドです。
        /// </summary>
        /// <param name="args">起動時引数</param>
        static void Main(string[] args) {

            new TagCreatorFactory(args).CreateCreator().Create();
        }
    }

    /// <summary>
    /// 何かを作成するというインターフェースを定義しています。
    /// </summary>
    public interface ICreator{

        /// <summary>
        /// 作成します。
        /// </summary>
        void Create();
    }

    /// <summary>
    /// ICreatorインタフェースの実装オブジェクトの生成を担当するファクトリクラスです。
    /// </summary>
    public interface ICreatorFactory {

        /// <summary>
        /// ICreatorインスタンスを生成します。
        /// </summary>
        /// <returns>ICreatorインスタンス</returns>
        ICreator CreateCreator();

    }

    /// <summary>
    /// タグデータの作成インタフェースを定義しています。
    /// </summary>
    public interface ITagDataCreator : ICreator{

        /// <summary>
        /// 処理対象に含める拡張子を取得・設定します。
        /// </summary>
        string[] IncludeSuffix {
            get;
            set;
        }

        /// <summary>
        /// 処理基底ディレクトリを取得・設定します。
        /// </summary>
        string BaseDirectory {
            get;
            set;
        }

        /// <summary>
        /// タグパターンを取得・設定します。
        /// </summary>
        string TagPattern {
            get;
            set;
        }

        /// <summary>
        /// 出力ディレクトリを取得・設定します。
        /// </summary>
        string OutputDirectory {
            get;
            set;
        } 
    }

    /// <summary>
    /// ICreatorインターフェースのデフォルト実装クラスです。
    /// </summary>
    /// <remarks>
    /// タグデータの作成を担当します。
    /// このクラスは、実行に際してITagDataCreatorインターフェースの実装を
    /// 要求します。
    /// 内部では、ITagDataCreatorの実装に処理を委譲しているだけです。
    /// </remarks>
    public class DefaultTagCreator : ICreator {

        /// <summary>
        /// タグ生成処理を担当する処理オブジェクト
        /// </summary>
        ITagDataCreator _tagDataCreator;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="tagDataCreator">タグ生成処理ロジックオブジェクト</param>
        public DefaultTagCreator(ITagDataCreator tagDataCreator) {
            _tagDataCreator = tagDataCreator;
        }

        /// <summary>
        /// タグデータの生成を行います。
        /// </summary>
        public void Create() {
            _tagDataCreator.Create();
        }
    }

    /// <summary>
    /// ITagDataCreatorインターフェースのデフォルト実装クラスです。
    /// </summary>
    /// <remarks>
    /// デフォルトで、このクラスはvim用のタグデータを生成します。
    /// 他のタグデータを出力する場合は別途実装クラスを作成します。
    /// その際にこのクラスをベースクラスとして作成すると便利です。
    /// </remarks>
    public class DefaultTagDataCreator : ITagDataCreator {

        /// <summary>
        /// 処理対象に含める拡張子
        /// </summary>
        string[] _includeSuffix;

        /// <summary>
        /// 処理基底ディレクトリ
        /// </summary>
        string   _baseDirectory;

        /// <summary>
        /// タグパターン
        /// </summary>
        string   _tagPattern;

        /// <summary>
        /// 出力ディレクトリ
        /// </summary>
        string   _outputDirectory;

        /// <seealso cref="ITagDataCreator.IncludeSuffix"/>
        public string[] IncludeSuffix {
            get {
                return _includeSuffix;
            }
            set {
                _includeSuffix = value;
            }
        }

        /// <seealso cref="ITagDataCreator.BaseDirectory"/>
        public string BaseDirectory {
            get {
                return _baseDirectory;
            }
            set {
                _baseDirectory = value;
            }
        }

        /// <seealso cref="ITagDataCreator.TagPattern"/>
        public string TagPattern {
            get {
                return _tagPattern;
            }
            set {
                _tagPattern = value;
            }
        }

        /// <seealso cref="ITagDataCreator.OutputDirectory"/>
        public string OutputDirectory {
            get {
                return _outputDirectory;
            }
            set {
                _outputDirectory = value;
            }
        }

        /// <summary>
        /// 指定されたファイルパスの拡張子が妥当なものかどうかを判断します。
        /// </summary>
        /// <param name="filePath">ファイルパス</param>
        /// <returns>妥当な拡張子の場合true, それ以外はfalse</returns>
        protected virtual bool IsValidSuffix(string filePath) {

            foreach(string suffix in _includeSuffix) {
                if(filePath.EndsWith(suffix)) {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 指定されたファイルパスからタグデータを取得します。
        /// </summary>
        /// <param name="filePath">ファイルパス</param>
        /// <returns>タグデータのリスト</returns>
        protected virtual List<string> GetTagData(string filePath) {
            List<string> result = new List<string>();
            Regex        regex  = new Regex(_tagPattern);

            using(StreamReader sr = new StreamReader(filePath)) {

                int count = 0;
                for(string line = sr.ReadLine(); line != null; line = sr.ReadLine(), count++) {

                    Match m = regex.Match(line);
                    if(m.Success) {
                        result.Add(GetFormatString(m, filePath, line, count));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 指定されたデータから適切なタグデータを生成し、返します。
        /// </summary>
        /// <param name="m">正規表現マッチオブジェクト</param>
        /// <param name="filePath">ファイルパス</param>
        /// <param name="line">行データ</param>
        /// <param name="lineCount">行数</param>
        /// <returns>フォーマット済みのタグデータ</returns>
        protected virtual string GetFormatString(Match m, string filePath, string line, int lineCount) {
            return string.Format("{0}\t{1}\t/^{2}$/;\"\tf", m.Groups[2], filePath, line);
        }

        /// <summary>
        /// タグデータを生成します。
        /// </summary>
        public virtual void Create() {
            if(_baseDirectory == null || _baseDirectory.Length == 0) {
                _baseDirectory = ".";
            }

            _baseDirectory = Path.GetFullPath(_baseDirectory);

            Console.WriteLine("[処理開始] 対象基底ディレクトリ:{0}", _baseDirectory);

            List<string> result = new List<string>();
            foreach(string f in Directory.GetFiles(_baseDirectory)) {
                if(!IsValidSuffix(f)) {
                    continue;
                }

                result.AddRange(GetTagData(f));
            }

            foreach(string subDirectory in Directory.GetDirectories(_baseDirectory, "*", SearchOption.AllDirectories)) {
                foreach(string f in Directory.GetFiles(subDirectory)) {
                    if(!IsValidSuffix(f)) {
                        continue;
                    }

                    result.AddRange(GetTagData(f));
                }
            }

            result.Sort();

            using(StreamWriter writer = new StreamWriter(Path.Combine(_outputDirectory, "tags"))) {
                foreach(string element in result) {
                    writer.WriteLine(element);
                }
            }
        }
    }

    /// <summary>
    /// Sakuraエディタ用のタグデータを生成するタグデータ生成クラスです。
    /// </summary>
    public class SakuraTagDataCreator : DefaultTagDataCreator {

        /// <summary>
        /// データを適切な書式にフォーマットして返します。
        /// </summary>
        /// <param name="m">正規表現マッチオブジェクト</param>
        /// <param name="filePath">ファイルパス</param>
        /// <param name="line">行データ</param>
        /// <param name="lineCount">行数</param>
        /// <returns>フォーマット済みのデータ</returns>
        protected override string GetFormatString(Match m, string filePath, string line, int lineCount) {
            return string.Format("{0}\t{1}\t{2};\"\tf", m.Groups[2], filePath, lineCount);
        }
    }

    /// <summary>
    /// タグデータ生成クラスの生成を担当するファクトリクラスです。
    /// </summary>
    public class TagCreatorFactory : ICreatorFactory{

        /// <summary>
        /// 起動時引数
        /// </summary>
        string[] _args;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="args">起動時引数</param>
        public TagCreatorFactory(string[] args) {
            _args = args;
        }

        #region ICreatorFactory メンバ

        /// <summary>
        /// 指定されたデータに基づいて処理を行うタグデータ生成クラスを特定し、返します。
        /// </summary>
        /// <returns>タグデータ生成オブジェクト</returns>
        public ICreator CreateCreator() {

            ICreator        creator        = null;
            ITagDataCreator tagDataCreator = null;

            tagDataCreator = new DefaultTagDataCreator();
            if(_args.Length == 2) {
                tagDataCreator = new SakuraTagDataCreator();
            }

            string directory = string.IsNullOrEmpty(_args[0]) ? "." : _args[0];

            tagDataCreator.IncludeSuffix = new string[]{
                "frm"
               ,"bas"
               ,"cls"
            };
            tagDataCreator.BaseDirectory   = directory;
            tagDataCreator.TagPattern      = "(Sub|Function|Const)\\s+([^\\s]+)\\s*(\\(|=)";
            tagDataCreator.OutputDirectory = directory;

            creator = new DefaultTagCreator(tagDataCreator);

            return creator;
        }

        #endregion
    }
}

他のライブラリを使用していないので、
上のソースを

csc ソースファイル

とすれば、コンパイルできます。
.net framework 2.0が必要になります。


現状、vimとsakuraエディタの形式に対応しています。

VBTags c:\tmp

とすればvim用で、

VBTags c:\tmp -sakura

とすればsakura用のタグファイルが出来上がります。



[追記]
GUI版つくってみました。
以下の記事を参照ねがいます。
http://d.hatena.ne.jp/gsf_zero1/20070409/p1



================================
過去の記事については、以下のページからご参照下さい。