|
| 1 | +#!/usr/bin/env moon |
| 2 | + |
| 3 | +HEADER=[[ |
| 4 | +!_TAG_FILE_FORMAT2/extended format/ |
| 5 | +!_TAG_FILE_SORTED1/0=unsorted, 1=sorted, 2=foldcase/ |
| 6 | +!_TAG_PROGRAM_AUTHORleaf corcoran/leafot@gmail.com/ |
| 7 | +!_TAG_PROGRAM_NAMEMoonTags// |
| 8 | +!_TAG_PROGRAM_URLhttps://github.com/leafo/moonscript/GitHub repository/ |
| 9 | +!_TAG_PROGRAM_VERSION0.0.1// |
| 10 | +]] |
| 11 | + |
| 12 | +-- see `ctags --list-kinds` for examples of kinds |
| 13 | +-- see `ctags --list-fields` |
| 14 | + |
| 15 | +argparse=require"argparse" |
| 16 | + |
| 17 | +parser= argparse"moon-tags","Generate ctags style tags file for MoonScript files" |
| 18 | +parser\argument("files","MoonScript files to generate tags for")\args"+" |
| 19 | +parser\flag"--include-line","Include line number field for each tag" |
| 20 | + |
| 21 | +args= parser\parse[vfor _, vinipairs_G.arg] |
| 22 | + |
| 23 | +TAGS={}-- the final output of tags |
| 24 | + |
| 25 | +literals=require"moonscript.parse.literals" |
| 26 | +importIndentfromrequire"moonscript.parse.util" |
| 27 | + |
| 28 | +importP,S,C,Cc,Cg,Cb,Ct,Cs,Vfromrequire"lpeg" |
| 29 | + |
| 30 | +-- consome the rest of the file |
| 31 | +until_end=(1- literals.Stop)^0 |
| 32 | +whitespace=S"\t"-- not including newline |
| 33 | +ignore_line=Ct until_end-- tag it for empty line |
| 34 | + |
| 35 | +-- we have to do this double Ct to capture both the full line and the grouped captures |
| 36 | +Line=(p)->CtCCtCg(Indent,"indent")* p |
| 37 | +Type=(name)->CgCc(name),"type" |
| 38 | + |
| 39 | +class_line=LineP"class"* whitespace^1*Cg(literals.Name,"tag")* until_end*Type"class" |
| 40 | +-- TODO: support lapis style routes |
| 41 | +-- class_property = Line P("@")^-1 * Cg(literals.Name, "tag") * P":" * until_end * Type "property" |
| 42 | + |
| 43 | +method=P{P"=>"+P(1- literals.Stop)*V(1)} |
| 44 | +class_method=LineP("@")^-1*Cg(literals.Name,"tag")*P":"* method* until_end*Type"method" |
| 45 | + |
| 46 | +parse_lines=CtP{ |
| 47 | +(class_line+ class_method+ ignore_line)*(P(-1)+ literals.Break*V(1)) |
| 48 | +} |
| 49 | + |
| 50 | +escape_tagaddress=(line_text)-> |
| 51 | + replacements=P([[\]])/[[\\]]+P([[/]])/[[\/]]+P("\t")/[[\t]]+P("\r")/[[\r]]+P("\n")/[[\n]] |
| 52 | +Cs((replacements+1)^0)\match line_text |
| 53 | + |
| 54 | +for fnamein*args.files |
| 55 | + file=assertio.open fname |
| 56 | + contents=assert file\read"*a" |
| 57 | + lines=assert parse_lines\match contents |
| 58 | + |
| 59 | + class_stack={} |
| 60 | + |
| 61 | + push_class=(cls)-> |
| 62 | +assert cls.type=="class","not a class match" |
| 63 | +-- remove classes that are longer in scope due to indentation |
| 64 | +for i=#class_stack,1,-1 |
| 65 | + top= class_stack[i] |
| 66 | + |
| 67 | +if cls.indent<= top.indent |
| 68 | +table.remove class_stack, i |
| 69 | +else |
| 70 | +break |
| 71 | + |
| 72 | +table.insert class_stack, cls |
| 73 | + |
| 74 | +-- find the class this property is associated with based on change in indent |
| 75 | +-- the expeted indent is written to `step` on the first proprety |
| 76 | + find_class=(property)-> |
| 77 | +for i=#class_stack,1,-1 |
| 78 | + top= class_stack[i] |
| 79 | + step= property.indent- top.indent |
| 80 | + |
| 81 | +if step>0 |
| 82 | +if top.step==nil |
| 83 | + top.step= step |
| 84 | + |
| 85 | +if step== top.step |
| 86 | +return top |
| 87 | + |
| 88 | +for line_no, lineinipairs lines |
| 89 | +continueunlessnext line |
| 90 | + |
| 91 | +{line_text, properties}= line |
| 92 | + |
| 93 | + fields={"language:moon"} |
| 94 | +if args.include_line |
| 95 | +table.insert fields,1,"line:#{line_no}" |
| 96 | + |
| 97 | +switch properties.type |
| 98 | +when"method" |
| 99 | +if cls= find_class properties |
| 100 | +table.insert fields,"class:#{cls.tag}" |
| 101 | + |
| 102 | +table.insertTAGS,{ |
| 103 | + properties.tag |
| 104 | + fname |
| 105 | +"/^#{escape_tagaddress line_text}$/;\"" |
| 106 | +"f" |
| 107 | +table.concat fields,"" |
| 108 | +} |
| 109 | +when"class" |
| 110 | + push_class properties |
| 111 | + |
| 112 | +table.insertTAGS,{ |
| 113 | + properties.tag |
| 114 | + fname |
| 115 | +"/^#{escape_tagaddress line_text}$/;\"" |
| 116 | +"c" |
| 117 | +table.concat fields,"" |
| 118 | +} |
| 119 | + |
| 120 | +printHEADER |
| 121 | +tag_lines=[table.concat(t,"\t")for tin*TAGS] |
| 122 | +table.sort tag_lines |
| 123 | +printtable.concat tag_lines,"\n" |