add moon-tags script for generating tag file for moonscript classes

This commit is contained in:
leaf corcoran 2023-06-22 15:33:50 -07:00
parent fa104985a6
commit 00397fd64b

123
bin/moon-tags Executable file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env moon
HEADER = [[
!_TAG_FILE_FORMAT 2 /extended format/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR leaf corcoran /leafot@gmail.com/
!_TAG_PROGRAM_NAME MoonTags //
!_TAG_PROGRAM_URL https://github.com/leafo/moonscript /GitHub repository/
!_TAG_PROGRAM_VERSION 0.0.1 //
]]
-- see `ctags --list-kinds` for examples of kinds
-- see `ctags --list-fields`
argparse = require "argparse"
parser = argparse "moon-tags", "Generate ctags style tags file for MoonScript files"
parser\argument("files", "MoonScript files to generate tags for")\args "+"
parser\flag "--include-line", "Include line number field for each tag"
args = parser\parse [v for _, v in ipairs _G.arg]
TAGS = {} -- the final output of tags
literals = require "moonscript.parse.literals"
import Indent from require "moonscript.parse.util"
import P, S, C, Cc, Cg, Cb, Ct, Cs, V from require "lpeg"
-- consome the rest of the file
until_end = (1 - literals.Stop)^0
whitespace = S"\t " -- not including newline
ignore_line = Ct until_end -- tag it for empty line
-- we have to do this double Ct to capture both the full line and the grouped captures
Line = (p) -> Ct C Ct Cg(Indent, "indent") * p
Type = (name) -> Cg Cc(name), "type"
class_line = Line P"class" * whitespace^1 * Cg(literals.Name, "tag") * until_end * Type "class"
-- TODO: support lapis style routes
-- class_property = Line P("@")^-1 * Cg(literals.Name, "tag") * P":" * until_end * Type "property"
method = P { P"=>" + P(1 - literals.Stop) * V(1) }
class_method = Line P("@")^-1 * Cg(literals.Name, "tag") * P":" * method * until_end * Type "method"
parse_lines = Ct P {
(class_line + class_method + ignore_line) * (P(-1) + literals.Break * V(1))
}
escape_tagaddress = (line_text) ->
replacements = P([[\]]) / [[\\]] + P([[/]]) / [[\/]] + P("\t") / [[\t]] + P("\r") / [[\r]] + P("\n") / [[\n]]
Cs((replacements + 1)^0)\match line_text
for fname in *args.files
file = assert io.open fname
contents = assert file\read "*a"
lines = assert parse_lines\match contents
class_stack = {}
push_class = (cls) ->
assert cls.type == "class", "not a class match"
-- remove classes that are longer in scope due to indentation
for i=#class_stack,1,-1
top = class_stack[i]
if cls.indent <= top.indent
table.remove class_stack, i
else
break
table.insert class_stack, cls
-- find the class this property is associated with based on change in indent
-- the expeted indent is written to `step` on the first proprety
find_class = (property) ->
for i=#class_stack,1,-1
top = class_stack[i]
step = property.indent - top.indent
if step > 0
if top.step == nil
top.step = step
if step == top.step
return top
for line_no, line in ipairs lines
continue unless next line
{line_text, properties} = line
fields = {"language:moon"}
if args.include_line
table.insert fields, 1, "line:#{line_no}"
switch properties.type
when "method"
if cls = find_class properties
table.insert fields, "class:#{cls.tag}"
table.insert TAGS, {
properties.tag
fname
"/^#{escape_tagaddress line_text}$/;\""
"f"
table.concat fields, " "
}
when "class"
push_class properties
table.insert TAGS, {
properties.tag
fname
"/^#{escape_tagaddress line_text}$/;\""
"c"
table.concat fields, " "
}
print HEADER
tag_lines = [table.concat(t, "\t") for t in *TAGS]
table.sort tag_lines
print table.concat tag_lines, "\n"