gitlab-glfm-markdown 0.0.4 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,6 +1,13 @@
1
- The MIT License (MIT)
1
+ Copyright (c) 2011-present GitLab B.V.
2
2
 
3
- Copyright (c) 2022-present GitLab B.V.
3
+ Portions of this software are licensed as follows:
4
+
5
+ * All content residing under the "doc/" directory of this repository is licensed under "Creative Commons: CC BY-SA 4.0 license".
6
+ * All content that resides under the "ee/" directory of this repository, if that directory exists, is licensed under the license defined in "ee/LICENSE".
7
+ * All content that resides under the "jh/" directory of this repository, if that directory exists, is licensed under the license defined in "jh/LICENSE".
8
+ * All client-side JavaScript (when served directly or after being compiled, arranged, augmented, or combined), is licensed under the "MIT Expat" license.
9
+ * All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component.
10
+ * Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below.
4
11
 
5
12
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
13
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # GitLab Flavored Markdown
2
2
 
3
+ [![Pipeline status](https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/badges/main/pipeline.svg)](https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/-/commits/main)
4
+ [![Latest Release](https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/-/badges/release.svg)](https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/-/releases)
5
+
3
6
  Implements GLFM (as used by GitLab) using a Rust-based markdown parser.
4
7
 
5
8
  This project is currently EXPLORATORY, so anything and everything will change.
@@ -22,7 +25,7 @@ Try on command line:
22
25
  rake compile
23
26
  bin/console
24
27
 
25
- GLFMMarkdown.to_html('# header', options: {sourcepos: true})
28
+ GLFMMarkdown.to_html('# header', options: { glfm: true })
26
29
  ```
27
30
 
28
31
  ## Development
@@ -30,12 +33,32 @@ GLFMMarkdown.to_html('# header', options: {sourcepos: true})
30
33
  A command line executable can be built for debugging.
31
34
 
32
35
  ```
33
- cd ext/glfm_markdown
34
- cargo run --bin glfm_markdown -- --sourcepos
36
+ cargo run --bin glfm_markdown --features="cli" -- --help
37
+ cargo run --bin glfm_markdown --features="cli" -- --sourcepos
35
38
  ```
36
39
 
37
40
  There is a VSCode workspace that allows you to `Debug executable`
38
41
 
42
+ When developing another project locally and using `gitlab-glfm-markdown` by linking
43
+ directly to the gem's source directory, make sure that you're using the same version
44
+ of Ruby for the project and the gem. Otherwise you can see unexplained errors when
45
+ calling into the gem.
46
+
47
+ ### Releasing a new version
48
+
49
+ To release a new version:
50
+
51
+ 1. Update `lib/glfm_markdown/version.rb` with the version number.
52
+ 1. Update `CHANGELOG.md` with the changes in the release.
53
+ 1. Create a merge request and merge it to `master`.
54
+ 1. Push a new tag to the repository.
55
+
56
+ The new version with precompiled, native gems will automatically be
57
+ published to [RubyGems](https://rubygems.org/gems/gitlab-glfm-markdown) when the
58
+ pipeline for the tag completes.
59
+
39
60
  ## Contributing
40
61
 
41
62
  Bug reports and merge requests are welcome on GitLab at https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown.
63
+
64
+ Please refer to [CONTRIBUTING](CONTRIBUTING.md) for more details.
@@ -1,8 +1,9 @@
1
1
  [package]
2
2
  name = "glfm_markdown"
3
- version = "0.0.3"
3
+ version = "0.0.8"
4
4
  edition = "2021"
5
5
  authors = ["digitalmoksha <bwalker@gitlab.com>"]
6
+ description = "GitLab Flavored Markdown parser and formatter. 100% CommonMark-compatible. Experimental."
6
7
  publish = false
7
8
 
8
9
  [lib]
@@ -10,8 +11,13 @@ crate-type = ["cdylib"]
10
11
 
11
12
  [[bin]]
12
13
  name = "glfm_markdown"
14
+ required-features = ["cli"]
13
15
 
14
16
  [dependencies]
15
- magnus = { version = "0.4" }
16
- argparse = { version = ">= 0.2.1" }
17
- comrak = { git = "https://github.com/kivikakk/comrak.git" }
17
+ clap = { version = "4.0", optional = true, features = ["derive", "string"] }
18
+ comrak = { version = "0.20.0", default-features = false }
19
+ magnus = "0.6.2"
20
+ rb-sys = { version = "0.9.83", default-features = false, features = ["stable-api-compiled-fallback"] }
21
+
22
+ [features]
23
+ cli = ["clap", "comrak/syntect"]
@@ -3,4 +3,6 @@
3
3
  require 'mkmf'
4
4
  require 'rb_sys/mkmf'
5
5
 
6
- create_rust_makefile('glfm_markdown/glfm_markdown')
6
+ create_rust_makefile('glfm_markdown') do |r|
7
+ r.auto_install_rust_toolchain = false
8
+ end
@@ -1,6 +1,23 @@
1
1
  #[derive(Debug)]
2
2
  pub struct RenderOptions {
3
+ pub autolink: bool,
4
+ pub escape: bool,
5
+ pub description_lists: bool,
6
+ pub footnotes: bool,
7
+ pub full_info_string: bool,
8
+ pub github_pre_lang: bool,
9
+ pub hardbreaks: bool,
10
+ pub relaxed_autolinks: bool,
11
+ pub relaxed_tasklist_character: bool,
3
12
  pub sourcepos: bool,
13
+ pub smart: bool,
14
+ pub strikethrough: bool,
15
+ pub superscript: bool,
16
+ pub table: bool,
17
+ pub tagfilter: bool,
18
+ pub tasklist: bool,
19
+ pub unsafe_: bool,
20
+
4
21
  pub debug: bool,
5
22
  }
6
23
 
@@ -11,20 +28,25 @@ pub fn render(text: String, options: RenderOptions) -> String {
11
28
  fn render_comrak(text: String, options: RenderOptions) -> String {
12
29
  let mut comrak_options = comrak::ComrakOptions::default();
13
30
 
14
- comrak_options.extension.strikethrough = true;
15
- comrak_options.extension.table = true;
16
- comrak_options.extension.autolink = true;
17
- comrak_options.extension.tasklist = false;
18
- comrak_options.extension.footnotes = true;
31
+ comrak_options.extension.autolink = options.autolink;
32
+ comrak_options.extension.description_lists = options.description_lists;
33
+ comrak_options.extension.footnotes = options.footnotes;
34
+ comrak_options.extension.strikethrough = options.strikethrough;
35
+ comrak_options.extension.superscript = options.superscript;
36
+ comrak_options.extension.table = options.table;
37
+ comrak_options.extension.tagfilter = options.tagfilter;
38
+ comrak_options.extension.tasklist = options.tasklist;
19
39
 
20
- comrak_options.render.unsafe_ = true;
21
- comrak_options.render.github_pre_lang = true;
22
- comrak_options.render.full_info_string = true;
23
- comrak_options.render.hardbreaks = false;
40
+ comrak_options.render.escape = options.escape;
41
+ comrak_options.render.full_info_string = options.full_info_string;
42
+ comrak_options.render.github_pre_lang = options.github_pre_lang;
43
+ comrak_options.render.hardbreaks = options.hardbreaks;
24
44
  comrak_options.render.sourcepos = options.sourcepos;
45
+ comrak_options.render.unsafe_ = options.unsafe_;
25
46
 
26
- comrak_options.parse.smart = false;
27
- comrak_options.parse.relaxed_autolinks = false;
47
+ comrak_options.parse.relaxed_autolinks = options.relaxed_autolinks;
48
+ comrak_options.parse.relaxed_tasklist_matching = options.relaxed_tasklist_character;
49
+ comrak_options.parse.smart = options.smart;
28
50
 
29
51
  comrak::markdown_to_html(&text, &comrak_options)
30
52
  }
@@ -3,10 +3,33 @@ use magnus::{define_module, function, prelude::*, Error, RHash, Symbol};
3
3
  mod glfm;
4
4
  use glfm::{render, RenderOptions};
5
5
 
6
- pub fn render_to_html(text: String, options: RHash) -> String {
7
- let sourcepos: bool = options.lookup::<_, bool>(Symbol::new("sourcepos")).unwrap();
8
- let debug: bool = options.lookup::<_, bool>(Symbol::new("debug")).unwrap();
9
- let render_options = RenderOptions { sourcepos, debug };
6
+ /// Lookup symbol in provided `RHash`. Returns `false` if the key is not present
7
+ /// or value cannot be converted to a boolean.
8
+ fn get_opt(arg: &str, options: RHash) -> bool {
9
+ options.lookup(Symbol::new(arg)).unwrap_or_default()
10
+ }
11
+
12
+ pub fn render_to_html_rs(text: String, options: RHash) -> String {
13
+ let render_options = RenderOptions {
14
+ autolink: get_opt("autolink", options),
15
+ description_lists: get_opt("description_lists", options),
16
+ escape: get_opt("escape", options),
17
+ footnotes: get_opt("footnotes", options),
18
+ full_info_string: get_opt("full_info_string", options),
19
+ github_pre_lang: get_opt("github_pre_lang", options),
20
+ hardbreaks: get_opt("hardbreaks", options),
21
+ relaxed_autolinks: get_opt("relaxed_autolinks", options),
22
+ relaxed_tasklist_character: get_opt("relaxed_tasklist_character", options),
23
+ sourcepos: get_opt("sourcepos", options),
24
+ smart: get_opt("smart", options),
25
+ strikethrough: get_opt("strikethrough", options),
26
+ superscript: get_opt("superscript", options),
27
+ table: get_opt("table", options),
28
+ tagfilter: get_opt("tagfilter", options),
29
+ tasklist: get_opt("tasklist", options),
30
+ unsafe_: get_opt("unsafe", options),
31
+ debug: get_opt("debug", options),
32
+ };
10
33
 
11
34
  render(text, render_options)
12
35
  }
@@ -15,7 +38,7 @@ pub fn render_to_html(text: String, options: RHash) -> String {
15
38
  fn init() -> Result<(), Error> {
16
39
  let module = define_module("GLFMMarkdown")?;
17
40
 
18
- module.define_singleton_method("render_to_html", function!(render_to_html, 2))?;
41
+ module.define_singleton_method("render_to_html_rs", function!(render_to_html_rs, 2))?;
19
42
 
20
43
  Ok(())
21
44
  }
@@ -3,52 +3,132 @@ use glfm::{render, RenderOptions};
3
3
  use std::io::Read;
4
4
  use std::io::Write;
5
5
 
6
+ use clap::Parser;
7
+
8
+ #[derive(Parser, Debug)]
9
+ #[command(author, version, about, long_about = None)]
10
+ struct Args {
11
+ /// CommonMark file(s) to parse; or standard input if none passed
12
+ #[arg(value_name = "FILE")]
13
+ file: Option<String>,
14
+
15
+ /// Enable 'autolink' extension
16
+ #[arg(long)]
17
+ autolink: bool,
18
+
19
+ /// Enable 'description-lists' extension
20
+ #[arg(long)]
21
+ description_lists: bool,
22
+
23
+ /// Escape raw HTML instead of clobbering it
24
+ #[arg(long)]
25
+ escape: bool,
26
+
27
+ /// Enable 'footnotes' extension
28
+ #[arg(long)]
29
+ footnotes: bool,
30
+
31
+ /// Enable full info strings for code blocks
32
+ #[arg(long)]
33
+ full_info_string: bool,
34
+
35
+ /// Use GitHub-style <pre lang> for code blocks
36
+ #[arg(long)]
37
+ github_pre_lang: bool,
38
+
39
+ /// Treat newlines as hard line breaks
40
+ #[arg(long)]
41
+ hardbreaks: bool,
42
+
43
+ /// Write output to FILE instead of stdout
44
+ #[arg(short, long, value_name = "FILE")]
45
+ output: Option<String>,
46
+
47
+ /// Enable relaxing of autolink parsing, allowing links to be recognized when in brackets
48
+ #[arg(long)]
49
+ relaxed_autolinks: bool,
50
+
51
+ /// Enable relaxing which character is allowed in a tasklists
52
+ #[arg(long)]
53
+ relaxed_tasklist_character: bool,
54
+
55
+ /// Include source mappings in HTML attributes
56
+ #[arg(long)]
57
+ sourcepos: bool,
58
+
59
+ /// Use smart punctuation
60
+ #[arg(long)]
61
+ smart: bool,
62
+
63
+ /// Enable 'strikethrough' extension
64
+ #[arg(long)]
65
+ strikethrough: bool,
66
+
67
+ /// Enable 'superscript' extension
68
+ #[arg(long)]
69
+ superscript: bool,
70
+
71
+ /// Enable 'table' extension
72
+ #[arg(long)]
73
+ table: bool,
74
+
75
+ /// Enable 'tagfilter' extension
76
+ #[arg(long)]
77
+ tagfilter: bool,
78
+
79
+ /// Enable 'tasklist' extension
80
+ #[arg(long)]
81
+ tasklist: bool,
82
+
83
+ /// Allow raw HTML and dangerous URLs
84
+ #[arg(long = "unsafe")]
85
+ unsafe_: bool,
86
+
87
+ /// Show debug information
88
+ #[arg(long)]
89
+ debug: bool,
90
+ }
91
+
6
92
  fn main() {
7
- let mut input = "-".to_owned();
8
- let mut output = "-".to_owned();
9
- let mut sourcepos = false;
10
- let mut debug = false;
11
-
12
- {
13
- // this block limits scope of borrows by cli.refer() method
14
- let mut cli = argparse::ArgumentParser::new();
15
-
16
- cli.set_description("Gitlab Flavored Markdown. Experimental.");
17
-
18
- cli.refer(&mut input)
19
- .add_argument("file", argparse::Store, "File to read");
20
- cli.refer(&mut output)
21
- .add_option(&["-o", "--output"], argparse::Store, "File to write");
22
- cli.refer(&mut sourcepos).add_option(
23
- &["--sourcepos"],
24
- argparse::StoreTrue,
25
- "Include source mappings in HTML attributes.",
26
- );
27
- cli.refer(&mut debug).add_option(
28
- &["--debug"],
29
- argparse::StoreTrue,
30
- "Show debug information",
31
- );
32
-
33
- cli.parse_args_or_exit();
34
- }
35
-
36
- let vec = if input == "-" {
37
- let mut vec = Vec::new();
38
- std::io::stdin().read_to_end(&mut vec).unwrap();
39
- vec
40
- } else {
41
- std::fs::read(input).unwrap()
93
+ let mut s: Vec<u8> = Vec::with_capacity(2048);
94
+ let cli = Args::parse();
95
+
96
+ match cli.file {
97
+ None => {
98
+ std::io::stdin().read_to_end(&mut s).unwrap();
99
+ }
100
+ Some(fs) => {
101
+ s = std::fs::read(fs).unwrap();
102
+ }
42
103
  };
43
104
 
44
- let source = String::from_utf8_lossy(&vec);
45
- let options = RenderOptions { sourcepos, debug };
105
+ let source = String::from_utf8_lossy(&s);
106
+ let options = RenderOptions {
107
+ autolink: cli.autolink,
108
+ description_lists: cli.description_lists,
109
+ escape: cli.escape,
110
+ footnotes: cli.footnotes,
111
+ full_info_string: cli.full_info_string,
112
+ github_pre_lang: cli.github_pre_lang,
113
+ hardbreaks: cli.hardbreaks,
114
+ relaxed_autolinks: cli.relaxed_autolinks,
115
+ relaxed_tasklist_character: cli.relaxed_tasklist_character,
116
+ sourcepos: cli.sourcepos,
117
+ smart: cli.smart,
118
+ strikethrough: cli.strikethrough,
119
+ superscript: cli.superscript,
120
+ table: cli.table,
121
+ tagfilter: cli.tagfilter,
122
+ tasklist: cli.tasklist,
123
+ unsafe_: cli.unsafe_,
124
+ debug: cli.debug,
125
+ };
46
126
 
47
127
  let result = render(source.to_string(), options);
48
128
 
49
- if output == "-" {
50
- std::io::stdout().write_all(result.as_bytes()).unwrap();
129
+ if let Some(output_filename) = cli.output {
130
+ std::fs::write(output_filename, &result).unwrap();
51
131
  } else {
52
- std::fs::write(output, &result).unwrap();
53
- }
132
+ std::io::stdout().write_all(result.as_bytes()).unwrap();
133
+ };
54
134
  }
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ def load_rust_extension
4
+ ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
5
+ require_relative "./#{ruby_version}/glfm_markdown"
6
+ rescue LoadError
7
+ require 'glfm_markdown/glfm_markdown'
8
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GLFMMarkdown
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.8'
5
5
  end
data/lib/glfm_markdown.rb CHANGED
@@ -1,17 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'glfm_markdown/version'
4
- require_relative 'glfm_markdown/glfm_markdown'
4
+ require_relative 'glfm_markdown/loader'
5
5
 
6
- DEFAULT_OPTIONS = {
6
+ load_rust_extension
7
+
8
+ GLFM_DEFAULT_OPTIONS = {
9
+ autolink: true,
10
+ footnotes: true,
11
+ full_info_string: true,
12
+ github_pre_lang: false,
13
+ hardbreaks: false,
14
+ relaxed_autolinks: false,
7
15
  sourcepos: true,
16
+ smart: false,
17
+ strikethrough: true,
18
+ table: true,
19
+ tagfilter: false,
20
+ tasklist: true,
21
+ unsafe: true,
22
+
8
23
  debug: false
9
24
  }.freeze
10
25
 
11
26
  module GLFMMarkdown
12
27
  class << self
13
- def to_html(text, options: {})
14
- render_to_html(text, DEFAULT_OPTIONS.merge(options))
28
+ def to_html(markdown, options: {})
29
+ raise TypeError, 'markdown must be a String' unless markdown.is_a?(String)
30
+ raise TypeError, 'markdown must be UTF-8 encoded' unless markdown.encoding.name == "UTF-8"
31
+ raise TypeError, 'options must be a Hash' unless options.is_a?(Hash)
32
+
33
+ default_options = options[:glfm] ? GLFM_DEFAULT_OPTIONS : {}
34
+ options = options.merge(unsafe: true) if options[:tagfilter]
35
+
36
+ render_to_html_rs(markdown, default_options.merge(options))
15
37
  end
16
38
  end
17
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-glfm-markdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Walker
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-08 00:00:00.000000000 Z
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.9'
27
27
  - !ruby/object:Gem::Dependency
28
- name: pry
28
+ name: pry-byebug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.12.2
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.12.2
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.3'
83
- description: GLFM Markdown
83
+ description: Markdown processing for GitLab Flavored Markdown
84
84
  email:
85
85
  - bwalker@gitlab.com
86
86
  executables: []
@@ -97,13 +97,15 @@ files:
97
97
  - ext/glfm_markdown/src/lib.rs
98
98
  - ext/glfm_markdown/src/main.rs
99
99
  - lib/glfm_markdown.rb
100
+ - lib/glfm_markdown/loader.rb
100
101
  - lib/glfm_markdown/version.rb
101
102
  homepage: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown
102
- licenses: []
103
+ licenses:
104
+ - MIT
103
105
  metadata:
104
106
  homepage_uri: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown
105
107
  source_code_uri: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown
106
- changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/-/blob/main/CHANGELOG.md
108
+ changelog_uri: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glfm-markdown/-/releases
107
109
  post_install_message:
108
110
  rdoc_options: []
109
111
  require_paths:
@@ -112,14 +114,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
114
  requirements:
113
115
  - - ">="
114
116
  - !ruby/object:Gem::Version
115
- version: 3.0.0
117
+ version: '2.7'
116
118
  required_rubygems_version: !ruby/object:Gem::Requirement
117
119
  requirements:
118
120
  - - ">="
119
121
  - !ruby/object:Gem::Version
120
- version: 3.2.33
122
+ version: '0'
121
123
  requirements: []
122
- rubygems_version: 3.4.10
124
+ rubygems_version: 3.3.26
123
125
  signing_key:
124
126
  specification_version: 4
125
127
  summary: GLFM Markdown