• R/O
  • SSH
  • HTTPS

blogs: コミット


コミットメタ情報

リビジョン81 (tree)
日時2019-01-24 19:50:36
作者hirukawa_ryo

ログメッセージ

* flexmark-ext-highlight 0.1.2 タイトル指定を追加しました。クリップボードコピー機能を追加しました。HTML属性追加機能を追加しました。flexmarkのバージョンを 0.40.12 に変更しました。

変更サマリ

差分

--- flexmark-ext-highlight/trunk/src/main/java/net/osdn/blogs/flexmark/ext/highlight/HighlightRenderer.java (revision 80)
+++ flexmark-ext-highlight/trunk/src/main/java/net/osdn/blogs/flexmark/ext/highlight/HighlightRenderer.java (revision 81)
@@ -2,6 +2,8 @@
22
33 import java.util.HashSet;
44 import java.util.Set;
5+import java.util.regex.Matcher;
6+import java.util.regex.Pattern;
57
68 import com.codewaves.codehighlight.core.Highlighter;
79 import com.codewaves.codehighlight.core.Highlighter.HighlightResult;
@@ -20,7 +22,23 @@
2022 import com.vladsch.flexmark.util.sequence.BasedSequence;
2123
2224 public class HighlightRenderer implements NodeRenderer {
23- public static final AttributablePart CODE_CONTENT = new AttributablePart("FENCED_CODE_CONTENT");
25+ private static final AttributablePart CODE_CONTENT = new AttributablePart("FENCED_CODE_CONTENT");
26+ private static final String ADDITIONAL_CHARS = "";
27+ private static final String ATTRIBUTENAME = "[a-zA-Z" + ADDITIONAL_CHARS + "_:][a-zA-Z0-9" + ADDITIONAL_CHARS + ":._-]*";
28+ private static final String UNQUOTEDVALUE = "[^\"'=<>{}`\u0000-\u0020]+";
29+ private static final String SINGLEQUOTEDVALUE = "'[^']*'";
30+ private static final String DOUBLEQUOTEDVALUE = "\"[^\"]*\"";
31+ private static final String ATTRIBUTEVALUE = "(?:" + UNQUOTEDVALUE + "|" + SINGLEQUOTEDVALUE + "|" + DOUBLEQUOTEDVALUE + ")";
32+ private static final Pattern ATTRIBUTES_TAG = Pattern.compile(
33+ "\\{((?:[#.])|(?:" + "\\s*([#.]" + UNQUOTEDVALUE + "|" + ATTRIBUTENAME + ")\\s*(?:=\\s*(" + ATTRIBUTEVALUE + ")?" + ")?" + ")" +
34+ "(?:" + "\\s+([#.]" + UNQUOTEDVALUE + "|" + ATTRIBUTENAME + ")\\s*(?:=\\s*(" + ATTRIBUTEVALUE + ")?" + ")?" + ")*" + "\\s*)\\}$");
35+ private static final Pattern ATTRIBUTE = Pattern.compile("\\s*([#.]" + UNQUOTEDVALUE + "|" + ATTRIBUTENAME + ")\\s*(?:=\\s*(" + ATTRIBUTEVALUE + ")?" + ")?");
36+
37+ private static final String COPY_SCRIPT =
38+ "document.getSelection().selectAllChildren(event.target.parentNode.nextSibling);" +
39+ "document.execCommand('copy');" +
40+ "document.getSelection().removeAllRanges();" +
41+ "return false;";
2442
2543 private final boolean codeContentBlock;
2644 private final Highlighter highlighter = new Highlighter(new StyleRendererFactory() {
@@ -59,15 +77,72 @@
5977 */
6078
6179 String languageName = null;
80+ BasedSequence language = BasedSequence.NULL;
81+ BasedSequence title = BasedSequence.NULL;
82+ boolean isCopyable = false;
6283
6384 html.line();
64- html.srcPosWithTrailingEOL(node.getChars()).withAttr().tag("pre").openPre();
6585
6686 BasedSequence info = node.getInfo();
6787 if (info.isNotNull() && !info.isBlank()) {
68- BasedSequence language = node.getInfoDelimitedByAny(" ");
88+ Matcher matcher = ATTRIBUTES_TAG.matcher(info);
89+ if(matcher.find()) {
90+ BasedSequence attributesText = info.subSequence(matcher.start(1), matcher.end(1)).trim();
91+ if(!attributesText.isEmpty()) {
92+ Matcher attributeMatcher = ATTRIBUTE.matcher(attributesText);
93+ while (attributeMatcher.find()) {
94+ BasedSequence attributeName = attributesText.subSequence(attributeMatcher.start(1), attributeMatcher.end(1));
95+ if(attributeName.isNotNull() && attributeName.length() > 0) {
96+ if(attributeName.charAt(0) == '.') {
97+ BasedSequence cls = attributeName.subSequence(1);
98+ html.attr("class", cls);
99+ if(cls.startsWith("cop")) {
100+ isCopyable = true;
101+ }
102+ } else if(attributeName.charAt(0) == '#') {
103+ html.attr("id", attributeName.subSequence(1));
104+ } else {
105+ BasedSequence attributeValue = attributeMatcher.groupCount() == 1 || attributeMatcher.start(2) == -1 ? BasedSequence.NULL : attributesText.subSequence(attributeMatcher.start(2), attributeMatcher.end(2));
106+ boolean isQuoted = attributeValue.length() >= 2 && (attributeValue.charAt(0) == '"' && attributeValue.endCharAt(1) == '"' || attributeValue.charAt(0) == '\'' && attributeValue.endCharAt(1) == '\'');
107+ if(isQuoted) {
108+ attributeValue = attributeValue.midSequence(1, -1);
109+ }
110+ if(attributeValue.isNotNull()) {
111+ html.attr(attributeName, attributeValue);
112+ }
113+ }
114+ }
115+ }
116+ }
117+ info = info.subSequence(0, matcher.start()).trim();
118+ }
119+
120+ if (info.isNotNull() && !info.isBlank()) {
121+ int space = info.indexOfAny(" ");
122+ if (space == -1) {
123+ language = info;
124+ } else {
125+ language = info.subSequence(0, space);
126+ title = info.subSequence(space).trim();
127+ }
128+ languageName = language.unescape();
129+ }
130+ }
131+ html.srcPosWithTrailingEOL(node.getChars()).withAttr().tag("pre").openPre();
132+
133+ if(title.isNotNull() || isCopyable) {
134+ html.attr("class", "title").withAttr().tag("div");
135+ if(title.isNotNull()) {
136+ html.append(title);
137+ }
138+ if(isCopyable) {
139+ html.attr("class", "copy-button").attr("onclick", COPY_SCRIPT).withAttr().tag("span").tag("/span");
140+ }
141+ html.tag("/div");
142+ }
143+
144+ if(language.isNotNull()) {
69145 html.attr("class", context.getHtmlOptions().languageClassPrefix + language.unescape());
70- languageName = language.unescape();
71146 } else {
72147 String noLanguageClass = context.getHtmlOptions().noLanguageClass.trim();
73148 if (!noLanguageClass.isEmpty()) {
@@ -74,7 +149,6 @@
74149 html.attr("class", noLanguageClass);
75150 }
76151 }
77-
78152 html.srcPosWithEOL(node.getContentChars()).withAttr(CODE_CONTENT).tag("code");
79153 if (codeContentBlock) {
80154 context.renderChildren(node);
旧リポジトリブラウザで表示