cmark 0.3.1 → 0.3.2
raw patch · 9 files changed
+107/−90 lines, 9 filesdep +bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring
API changes (from Hackage documentation)
Files
- CMark.hsc +27/−27
- README.md +2/−2
- cbits/cmark_version.h +2/−2
- cbits/commonmark.c +53/−47
- cbits/inlines.c +6/−4
- cbits/man.c +3/−0
- cbits/utf8.c +2/−3
- changelog +8/−0
- cmark.cabal +4/−5
CMark.hsc view
@@ -26,13 +26,15 @@ import Foreign import Foreign.C.Types-import Foreign.C.String (CString, withCString)+import Foreign.C.String (CString) import qualified System.IO.Unsafe as Unsafe import GHC.Generics (Generic) import Data.Data (Data) import Data.Typeable (Typeable)-import Data.Text (Text, empty, unpack)+import Data.Text (Text, empty) import qualified Data.Text.Foreign as TF+import qualified Data.ByteString as B+import Data.Text.Encoding (encodeUtf8) import Control.Applicative ((<$>), (<*>)) #include <cmark.h>@@ -55,14 +57,14 @@ -- | Convert CommonMark formatted text to a structured 'Node' tree, -- which can be transformed or rendered using Haskell code. commonmarkToNode :: [CMarkOption] -> Text -> Node-commonmarkToNode opts s = io $ do+commonmarkToNode opts s = Unsafe.unsafePerformIO $ do nptr <- TF.withCStringLen s $! \(ptr, len) -> c_cmark_parse_document ptr len (combineOptions opts) fptr <- newForeignPtr c_cmark_node_free nptr withForeignPtr fptr toNode nodeToCommonmark :: [CMarkOption] -> Int -> Node -> Text-nodeToCommonmark opts width node = io $ do+nodeToCommonmark opts width node = Unsafe.unsafePerformIO $ do nptr <- fromNode node fptr <- newForeignPtr c_cmark_node_free nptr withForeignPtr fptr $ \ptr -> do@@ -73,14 +75,15 @@ -> [CMarkOption] -> Text -> Text-commonmarkToX renderer opts s = io $ TF.withCStringLen s $ \(ptr, len) -> do- let opts' = combineOptions opts- nptr <- c_cmark_parse_document ptr len opts'- fptr <- newForeignPtr c_cmark_node_free nptr- withForeignPtr fptr $ \p -> do- str <- renderer p opts'- t <- TF.peekCStringLen $! (str, c_strlen str)- return t+commonmarkToX renderer opts s = Unsafe.unsafePerformIO $+ TF.withCStringLen s $ \(ptr, len) -> do+ let opts' = combineOptions opts+ nptr <- c_cmark_parse_document ptr len opts'+ fptr <- newForeignPtr c_cmark_node_free nptr+ withForeignPtr fptr $ \p -> do+ str <- renderer p opts'+ t <- TF.peekCStringLen $! (str, c_strlen str)+ return t type NodePtr = Ptr () @@ -263,12 +266,12 @@ BLOCK_QUOTE -> c_cmark_node_new (#const CMARK_NODE_BLOCK_QUOTE) HTML literal -> do n <- c_cmark_node_new (#const CMARK_NODE_HTML)- c_cmark_node_set_literal n (fromtext literal)+ c_cmark_node_set_literal n =<< fromtext literal return n CODE_BLOCK info literal -> do n <- c_cmark_node_new (#const CMARK_NODE_CODE_BLOCK)- c_cmark_node_set_literal n (fromtext literal)- c_cmark_node_set_fence_info n (fromtext info)+ c_cmark_node_set_literal n =<< fromtext literal+ c_cmark_node_set_fence_info n =<< fromtext info return n LIST attr -> do n <- c_cmark_node_new (#const CMARK_NODE_LIST)@@ -290,41 +293,38 @@ STRONG -> c_cmark_node_new (#const CMARK_NODE_STRONG) LINK url title -> do n <- c_cmark_node_new (#const CMARK_NODE_LINK)- c_cmark_node_set_url n (fromtext url)- c_cmark_node_set_title n (fromtext title)+ c_cmark_node_set_url n =<< fromtext url+ c_cmark_node_set_title n =<< fromtext title return n IMAGE url title -> do n <- c_cmark_node_new (#const CMARK_NODE_IMAGE)- c_cmark_node_set_url n (fromtext url)- c_cmark_node_set_title n (fromtext title)+ c_cmark_node_set_url n =<< fromtext url+ c_cmark_node_set_title n =<< fromtext title return n TEXT literal -> do n <- c_cmark_node_new (#const CMARK_NODE_TEXT)- c_cmark_node_set_literal n (fromtext literal)+ c_cmark_node_set_literal n =<< fromtext literal return n CODE literal -> do n <- c_cmark_node_new (#const CMARK_NODE_CODE)- c_cmark_node_set_literal n (fromtext literal)+ c_cmark_node_set_literal n =<< fromtext literal return n INLINE_HTML literal -> do n <- c_cmark_node_new (#const CMARK_NODE_INLINE_HTML)- c_cmark_node_set_literal n (fromtext literal)+ c_cmark_node_set_literal n =<< fromtext literal return n SOFTBREAK -> c_cmark_node_new (#const CMARK_NODE_SOFTBREAK) LINEBREAK -> c_cmark_node_new (#const CMARK_NODE_LINEBREAK) mapM_ (\child -> fromNode child >>= c_cmark_node_append_child node) children return node -io :: IO a -> a-io = Unsafe.unsafePerformIO- totext :: CString -> IO Text totext str | str == nullPtr = return empty | otherwise = TF.peekCStringLen (str, c_strlen str) -fromtext :: Text -> CString-fromtext t = io $! withCString (unpack t) return+fromtext :: Text -> IO CString+fromtext t = B.useAsCString (encodeUtf8 t) return foreign import ccall "string.h strlen" c_strlen :: CString -> Int
README.md view
@@ -3,8 +3,8 @@ This package provides Haskell bindings for [libcmark], the reference parser for [CommonMark], a fully specified variant of Markdown.-It includes sources for [libcmark] (as of commit f40e098, implementing-version 0.18 of the spec) and does not require prior installation of+It includes sources for [libcmark] (version 0.19.0, implementing+version 0.19 of the spec) and does not require prior installation of the C library. cmark provides the following advantages over existing Markdown
cbits/cmark_version.h view
@@ -1,7 +1,7 @@ #ifndef CMARK_VERSION_H #define CMARK_VERSION_H -#define CMARK_VERSION ((0 << 16) | (18 << 8) | 1)-#define CMARK_VERSION_STRING "0.18.1"+#define CMARK_VERSION ((0 << 16) | (19 << 8) | 0)+#define CMARK_VERSION_STRING "0.19.0" #endif
cbits/commonmark.c view
@@ -49,35 +49,35 @@ static inline bool needs_escaping(escaping escape,- int32_t c,- unsigned char next_c,- struct render_state *state)+ int32_t c,+ unsigned char next_c,+ struct render_state *state) { if (escape == NORMAL) { return (c == '*' || c == '_' || c == '[' || c == ']' ||- c == '<' || c == '>' || c == '\\' || c == '`' ||- (c == '&' && isalpha(next_c)) ||- (c == '!' && next_c == '[') ||- (state->begin_line &&- (c == '-' || c == '+' || c == '#' || c == '=')) ||- (c == '#' && (isspace(next_c) || next_c == '\0')) ||- ((c == '.' || c == ')') &&- isdigit(state->buffer->ptr[state->buffer->size - 1])));+ c == '<' || c == '>' || c == '\\' || c == '`' ||+ (c == '&' && isalpha(next_c)) ||+ (c == '!' && next_c == '[') ||+ (state->begin_line &&+ (c == '-' || c == '+' || c == '#' || c == '=')) ||+ (c == '#' && (isspace(next_c) || next_c == '\0')) ||+ ((c == '.' || c == ')') &&+ isdigit(state->buffer->ptr[state->buffer->size - 1]))); } else if (escape == TITLE) { return (c == '`' || c == '<' || c == '>' || c == '"' ||- c == '\\');+ c == '\\'); } else if (escape == URL) { return (c == '`' || c == '<' || c == '>' || isspace(c) ||- c == '\\' || c == ')' || c == '(');+ c == '\\' || c == ')' || c == '('); } else { return false; } } static inline void out(struct render_state *state,- cmark_chunk str,- bool wrap,- escaping escape)+ cmark_chunk str,+ bool wrap,+ escaping escape) { unsigned char* source = str.data; int length = str.len;@@ -100,7 +100,7 @@ cmark_strbuf_putc(state->buffer, '\n'); if (state->need_cr > 1) { cmark_strbuf_put(state->buffer, state->prefix->ptr,- state->prefix->size);+ state->prefix->size); } } state->column = 0;@@ -111,12 +111,15 @@ while (i < length) { if (state->begin_line) { cmark_strbuf_put(state->buffer, state->prefix->ptr,- state->prefix->size);+ state->prefix->size); // note: this assumes prefix is ascii: state->column = state->prefix->size; } len = utf8proc_iterate(source + i, length - i, &c);+ if (len == -1) { // error condition+ return; // return without rendering rest of string+ } nextc = source[i + len]; if (c == 32 && wrap) { if (!state->begin_line) {@@ -124,7 +127,7 @@ state->column += 1; state->begin_line = false; state->last_breakable = state->buffer->size -- 1;+ 1; // skip following spaces while (source[i + 1] == ' ') { i++;@@ -167,7 +170,7 @@ // add newline, prefix, and remainder cmark_strbuf_putc(state->buffer, '\n'); cmark_strbuf_put(state->buffer, state->prefix->ptr,- state->prefix->size);+ state->prefix->size); cmark_strbuf_put(state->buffer, remainder.data, remainder.len); state->column = state->prefix->size + remainder.len; cmark_chunk_free(&remainder);@@ -254,8 +257,8 @@ } cmark_consolidate_text_nodes(node); return (strncmp(url,- (char*)node->as.literal.data,- node->as.literal.len) == 0);+ (char*)node->as.literal.data,+ node->as.literal.len) == 0); } // if node is a block node, returns node.@@ -265,7 +268,7 @@ { while (node && (node->type < CMARK_NODE_FIRST_BLOCK ||- node->type > CMARK_NODE_LAST_BLOCK)) {+ node->type > CMARK_NODE_LAST_BLOCK)) { node = node->parent; } return node;@@ -284,7 +287,7 @@ bool entering = (ev_type == CMARK_EVENT_ENTER); const char *info; const char *title;- char listmarker[64];+ cmark_strbuf listmarker = GH_BUF_INIT; char *emph_delim; int marker_width; @@ -293,14 +296,14 @@ // a following list. if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {- tmp = get_containing_block(node);- state->in_tight_list_item =- (tmp->type == CMARK_NODE_ITEM &&- cmark_node_get_list_tight(tmp->parent)) ||- (tmp &&- tmp->parent &&- tmp->parent->type == CMARK_NODE_ITEM &&- cmark_node_get_list_tight(tmp->parent->parent));+ tmp = get_containing_block(node);+ state->in_tight_list_item =+ (tmp->type == CMARK_NODE_ITEM &&+ cmark_node_get_list_tight(tmp->parent)) ||+ (tmp &&+ tmp->parent &&+ tmp->parent->type == CMARK_NODE_ITEM &&+ cmark_node_get_list_tight(tmp->parent->parent)); } switch (node->type) {@@ -316,7 +319,7 @@ cmark_strbuf_puts(state->prefix, "> "); } else { cmark_strbuf_truncate(state->prefix,- state->prefix->size - 2);+ state->prefix->size - 2); blankline(state); } break;@@ -347,11 +350,12 @@ // we ensure a width of at least 4 so // we get nice transition from single digits // to double- snprintf(listmarker, 63, "%d%s%s", list_number,- list_delim == CMARK_PAREN_DELIM ?- ")" : ".",- list_number < 10 ? " " : " ");- marker_width = strlen(listmarker);+ cmark_strbuf_printf(&listmarker,+ "%d%s%s", list_number,+ list_delim == CMARK_PAREN_DELIM ?+ ")" : ".",+ list_number < 10 ? " " : " ");+ marker_width = listmarker.size; } if (entering) { if (cmark_node_get_list_type(node->parent) ==@@ -359,17 +363,18 @@ lit(state, "* ", false); cmark_strbuf_puts(state->prefix, " "); } else {- lit(state, listmarker, false);- for (i=marker_width; i--;) {+ lit(state, (char *)listmarker.ptr, false);+ for (i = marker_width; i--;) { cmark_strbuf_putc(state->prefix, ' '); } } } else { cmark_strbuf_truncate(state->prefix,- state->prefix->size -- marker_width);+ state->prefix->size -+ marker_width); cr(state); }+ cmark_strbuf_free(&listmarker); break; case CMARK_NODE_HEADER:@@ -403,7 +408,7 @@ cmark_strbuf_puts(state->prefix, " "); out(state, node->as.code.literal, false, LITERAL); cmark_strbuf_truncate(state->prefix,- state->prefix->size - 4);+ state->prefix->size - 4); } else { numticks = longest_backtick_sequence(code) + 1; if (numticks < 3) {@@ -512,7 +517,7 @@ if (entering) { lit(state, "<", false); if (strncmp(cmark_node_get_url(node),- "mailto:", 7) == 0) {+ "mailto:", 7) == 0) { lit(state, (char *)cmark_node_get_url(node) + 7, false);@@ -577,9 +582,10 @@ if (CMARK_OPT_HARDBREAKS & options) { width = 0; }- struct render_state state =- { options, &commonmark, &prefix, 0, width,- 0, 0, true, false, false};+ struct render_state state = {+ options, &commonmark, &prefix, 0, width,+ 0, 0, true, false, false+ }; cmark_node *cur; cmark_event_type ev_type; cmark_iter *iter = cmark_iter_new(root);
cbits/inlines.c view
@@ -301,8 +301,10 @@ !utf8proc_is_space(after_char) && !utf8proc_is_punctuation(after_char)); if (c == '_') {- *can_open = left_flanking && !right_flanking;- *can_close = right_flanking && !left_flanking;+ *can_open = left_flanking &&+ (!right_flanking || utf8proc_is_punctuation(before_char));+ *can_close = right_flanking &&+ (!left_flanking || utf8proc_is_punctuation(after_char)); } else if (c == '\'' || c == '"') { *can_open = left_flanking && !right_flanking; *can_close = right_flanking;@@ -636,7 +638,7 @@ } cmark_strbuf_unescape(&buf);- return cmark_strbuf_detach(&buf);+ return buf.size == 0 ? NULL : cmark_strbuf_detach(&buf); } unsigned char *cmark_clean_title(cmark_chunk *title)@@ -660,7 +662,7 @@ } cmark_strbuf_unescape(&buf);- return cmark_strbuf_detach(&buf);+ return buf.size == 0 ? NULL : cmark_strbuf_detach(&buf); } // Parse an autolink or HTML tag.
cbits/man.c view
@@ -20,6 +20,9 @@ while (i < length) { len = utf8proc_iterate(source + i, length - i, &c);+ if (len == -1) { // error condition+ return; // return without rendering anything+ } switch(c) { case 46: if (beginLine) {
cbits/utf8.c view
@@ -172,8 +172,7 @@ case 3: uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6) + (str[2] & 0x3F);- if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||- (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000)) uc = -1; break; case 4: uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)@@ -182,7 +181,7 @@ break; } - if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))+ if (uc < 0) return -1; *dst = uc;
changelog view
@@ -1,3 +1,11 @@+cmark 0.3.2 (28 Apr 2015)++ * Updated libcmark C sources to 0.19.0.+ * Fixes to avoid warning for earlier ghc versions.+ * Refactored, removing the `io` convenience function.+ * Removed unnecessary `unsafePerformIO` on `fromtext`.+ * `fromtext`: use `ByteString.useAsCString` + explicit `encodeUtf8`.+ cmark 0.3.1 (29 Mar 2015) * Added nodeToCommonmark.
cmark.cabal view
@@ -1,14 +1,13 @@ name: cmark-version: 0.3.1+version: 0.3.2 synopsis: Fast, accurate CommonMark (Markdown) parser and renderer description: This package provides Haskell bindings for <https://github.com/jgm/cmark libcmark>, the reference parser for <http://commonmark.org CommonMark>, a fully specified variant of Markdown. It includes sources for- libcmark (as of commit f40e098, implementing version 0.18- of the CommonMark spec) and does not require prior- installation of the C library.+ libcmark (0.19.0) and does not require prior installation of the+ C library. . cmark provides the following advantages over existing Markdown libraries for Haskell:@@ -118,7 +117,7 @@ library exposed-modules: CMark- build-depends: base >=4.5 && < 4.9, text+ build-depends: base >=4.5 && < 4.9, text, bytestring if impl(ghc < 7.6) build-depends: ghc-prim >= 0.2 default-language: Haskell2010