diff --git a/Makefile b/Makefile index cc6b324..aa89d7a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: $(wildcard src/**/*.cr) $(wildcard lexers/*xml) $(wildcard styles/*xml) shard.yml - shards build -Dstrict_multi_assign -Dno_number_autocast + shards build -Dstrict_multi_assign -Dno_number_autocast -d --error-trace release: $(wildcard src/**/*.cr) $(wildcard lexers/*xml) $(wildcard styles/*xml) shard.yml shards build --release static: $(wildcard src/**/*.cr) $(wildcard lexers/*xml) $(wildcard styles/*xml) shard.yml diff --git a/src/lexer.cr b/src/lexer.cr index c18ab31..9496ca0 100644 --- a/src/lexer.cr +++ b/src/lexer.cr @@ -5,6 +5,22 @@ module Tartrazine bake_folder "../lexers", __DIR__ end + # Get the lexer object for a language name + # FIXME: support aliases, paths, mimetypes, etc + def self.lexer(name : String) : Lexer + Lexer.from_xml(LexerFiles.get("/#{name}.xml").gets_to_end) + end + + # Return a list of all lexers + # FIXME: support aliases + def self.lexers : Array(String) + lexers = Set(String).new + LexerFiles.files.each do |file| + lexers << file.path.split("/").last.split(".").first + end + lexers.to_a.sort! + end + # This implements a lexer for Pygments RegexLexers as expressed # in Chroma's XML serialization. # @@ -173,8 +189,4 @@ module Tartrazine # A token, the output of the tokenizer alias Token = NamedTuple(type: String, value: String) - - def self.lexer(name : String) : Lexer - Lexer.from_xml(LexerFiles.get("/#{name}.xml").gets_to_end) - end end diff --git a/src/main.cr b/src/main.cr index 418f228..9f7caf6 100644 --- a/src/main.cr +++ b/src/main.cr @@ -1,35 +1,92 @@ +require "docopt" require "./**" HELP = <<-HELP tartrazine: a syntax highlighting tool Usage: - - tartrazine FILE -f html [-t theme][--standalone][--line-numbers] - [-l lexer] [-o output][--css] + tartrazine (-h, --help) + tartrazine FILE -f html [-t theme][--standalone][--line-numbers][-l lexer] [-o output] + tartrazine -f html -t theme --css tartrazine FILE -f terminal [-t theme][-l lexer][-o output] tartrazine FILE -f json [-o output] tartrazine --list-themes tartrazine --list-lexers + tartrazine --list-formatters + tartrazine --version --f Format to use (html, terminal, json) --t Theme to use (see --list-themes) --l Lexer (language) to use (see --list-lexers) --o Output file (default: stdout) ---standalone Generate a standalone HTML file ---css Generate a CSS file for the theme ---line-numbers Include line numbers in the output +Options: + -f Format to use (html, terminal, json) + -t Theme to use, see --list-themes [default: default-dark] + -l Lexer (language) to use, see --list-lexers [default: plaintext] + -o Output file. Default is stdout. + --standalone Generate a standalone HTML file, which includes + all style information. If not given, it will generate just + a HTML fragment ready to include in your own page. + --css Generate a CSS file for the theme called .css + --line-numbers Include line numbers in the output + -h, --help Show this screen + -v, --version Show version number HELP -lexer = Tartrazine.lexer("crystal") -theme = Tartrazine.theme(ARGV[1]) -# formatter = Tartrazine::Json.new -formatter = Tartrazine::Html.new -formatter.standalone = true -formatter.class_prefix = "hl-" -formatter.line_number_id_prefix = "ln-" -formatter.line_numbers = true -formatter.highlight_lines = [3..7, 20..30] -formatter.linkable_line_numbers = false -formatter.wrap_long_lines = false -puts formatter.format(File.read(ARGV[0]), lexer, theme) +options = Docopt.docopt(HELP, ARGV) + +# Handle version manually +if options["--version"] + puts "tartrazine #{Tartrazine::VERSION}" + exit 0 +end + +if options["--list-themes"] + puts Tartrazine.themes.join("\n") + exit 0 +end + +if options["--list-lexers"] + puts Tartrazine.lexers.join("\n") + exit 0 +end + +if options["--list-formatters"] + puts "html\njson\nterminal" + exit 0 +end + +if options["-f"] + formatter = options["-f"].as(String) + case formatter + when "html" + formatter = Tartrazine::Html.new + formatter.standalone = options["--standalone"] != nil + formatter.line_numbers = options["--line-numbers"] != nil + when "terminal" + formatter = Tartrazine::Ansi.new + when "json" + formatter = Tartrazine::Json.new + else + puts "Invalid formatter: #{formatter}" + exit 1 + end + + theme = Tartrazine.theme(options["-t"].as(String)) + + if formatter.is_a?(Tartrazine::Html) && options["--css"] + File.open("#{options["-t"].as(String)}.css", "w") do |outf| + outf.puts formatter.get_style_defs(theme) + end + exit 0 + end + + lexer = Tartrazine.lexer(options["-l"].as(String)) + + input = File.open(options["FILE"].as(String)).gets_to_end + output = formatter.format(input, lexer, theme) + + if options["-o"].nil? + puts output + else + File.open(options["-o"].as(String), "w") do |outf| + outf.puts output + end + end +end diff --git a/src/styles.cr b/src/styles.cr index d84f1b5..1822377 100644 --- a/src/styles.cr +++ b/src/styles.cr @@ -10,6 +10,11 @@ require "xml" module Tartrazine alias Color = Sixteen::Color + class ThemeFiles + extend BakedFileSystem + bake_folder "../styles", __DIR__ + end + def self.theme(name : String) : Theme begin return Theme.from_base16(name) @@ -23,9 +28,16 @@ module Tartrazine end end - class ThemeFiles - extend BakedFileSystem - bake_folder "../styles", __DIR__ + # Return a list of all themes + def self.themes + themes = Set(String).new + ThemeFiles.files.each do |file| + themes << file.path.split("/").last.split(".").first + end + Sixteen::DataFiles.files.each do |file| + themes << file.path.split("/").last.split(".").first + end + themes.to_a.sort! end class Style