diff --git a/core/mint.json b/core/mint.json index f1d8f507a..123c3145d 100644 --- a/core/mint.json +++ b/core/mint.json @@ -1,5 +1,6 @@ { "name": "core", + "source-url": "https://github.com/mint-lang/mint", "source-directories": [ "source" ] diff --git a/src/assets/docs-html/app.css b/src/assets/docs-html/app.css new file mode 100644 index 000000000..76cd0b712 --- /dev/null +++ b/src/assets/docs-html/app.css @@ -0,0 +1,735 @@ +* { + box-sizing: border-box; + min-height: 0; + min-width: 0; +} + +html { + scrollbar-color: #999 #ddd; +} + +body { + font-family: Noto Sans; + flex-direction: column; + overflow-y: scroll; + background: whitesmoke; + min-height: 100vh; + display: flex; + color: #333; + margin: 0; +} + +article { + min-height: calc(100vh - 100px); + flex: 1; +} + +.sidebar a { + text-decoration: none; + color: #333; + align-items: center; + font-weight: 600; + display: flex; + height: 20px; + margin: 7px 0; +} + +.sidebar a .type-badge { + flex: 0 0 auto; +} + +.sidebar a span { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.sidebar a.active, +.sidebar a:hover { + color: #02b046; +} + +.guide .guide-content > *:first-child { + margin-top: 0; +} + +.guide .guide-content > *:last-child { + margin-bottom: 0; +} + +@media (max-width: 1200px) { + .guide .guide-content { + font-size: 16px; + } +} + +.guide .guide-content img { + max-width: 100%; +} + +.guide .guide-content h1, +.guide .guide-content h2, +.guide .guide-content h3, +.guide .guide-content h4, +.guide .guide-content h5 { + font-family: "Courgette", sans-serif; + font-weight: 700; + line-height: 1.5; + color: #333; + margin: 0; + margin-bottom: 20px; +} + +.guide .guide-content h1 { + position: relative; + border-bottom: 2px dashed #CCC; + padding-bottom: 0.3em; + font-size: 2em; +} + +.guide .guide-content h1 small { + font-weight: 500; + font-size: 14px; + display: block; + color: #999; +} + +.guide .guide-content h1 .type-badge { + position: relative; + top: -8px; + left: 16px; + scale: 1.5; +} + +.guide .guide-content h1 .github-source-link { + margin-top: 12px; +} + +@media (max-width: 1200px) { + .guide .guide-content h1 { + font-size: 1.7em; + } +} + +.guide .guide-content h2 { + border-bottom: 2px dashed #CCC; + padding-bottom: 0.3em; + margin-top: 2em; + font-size: 1.5em; +} + +.guide .guide-content h2 a { + color: #333; +} + +@media (max-width: 1200px) { + .guide .guide-content h2 { + font-size: 1.3em; + } +} + +.guide .guide-content h3 { + margin-top: 1.5em; + font-size: 1.25em; +} + +.guide .guide-content li > ul { + margin-top: 0; + margin-bottom: 0; +} + +.guide .guide-content li > pre, +.guide .guide-content li + li, +.guide .guide-content li > ul { + margin-top: 7px; +} + +.guide .guide-content ul { + padding-left: 30px; +} + +.guide .guide-content code:not(.language-mint) { + white-space: pre; + background: #eee; + padding: 3px 6px; + border-radius: 2px; +} + +.guide .guide-content b, +.guide .guide-content strong { + color: #333; +} + +.guide .guide-content p, +.guide .guide-content ul, +.guide .guide-content ol, +.guide .guide-content pre, +.guide .guide-content blockqoute { + margin: 0; + margin-bottom: 15px; +} + +.guide .guide-content a { + text-decoration: none; + color: #02b046; +} + +.guide .guide-content p.hint { + grid-template-columns: min-content 1fr; + grid-gap: 10px; + display: grid; + background: #eee; + padding: 20px; +} + +.guide .guide-content p.hint svg { + align-self: center; + margin-right: 10px; + height: 24px; + width: 24px; +} + +.guide .guide-content p.hint code:not(.language-mint) { + background: #ddd; +} + +@media (max-width: 1200px) { + .guide .guide-content p.hint { + grid-template-columns: 1fr; + } + .guide .guide-content p.hint svg { + display: none; + } +} + +.guide .guide-content p.hint--warning { + border-left: 4px solid orangered; +} + +.guide .guide-content p.hint--warning svg { + fill: orangered; +} + +.guide .guide-content p.hint--info { + border-left: 4px solid #3884ff; +} + +.guide .guide-content p.hint--info svg { + fill: #3884ff; +} + +.guide .guide-content table { + border-collapse: collapse; + width: 100%; +} + +.guide .guide-content table th, +.guide .guide-content table td { + text-align: left; + padding: 8px; +} + +.guide .guide-content table tr + tr td { + border-top: 1px solid #ccc; +} + +.guide .guide-content table thead th { + border-bottom: 2px solid #e6ecf1; + font-weight: bold; + color: #999; +} + +.guide .guide-content blockqoute { + border-left: 4px solid #ccc; + display: block; + padding: 20px; + color: #666; +} + +.guide .guide-content hr { + margin: 30px 0; + border: 0; + border-top: 3px double #ccc; +} + +.guide { + grid-template-columns: minmax(330px, 1fr) 900px 1fr; + padding-bottom: 100px; + display: grid; +} + +.guide--wide { + grid-template-columns: minmax(400px, 1fr) 900px 1fr; +} + +@media (max-width: 1330px) { + .guide { + grid-template-columns: 300px 1fr; + } +} + +@media (max-width: 1200px) { + .guide { + grid-template-columns: 1fr; + padding-bottom: 0; + } +} + +.page { + padding: 40px 60px; + max-width: 1620px; + margin: 0 auto; +} + +.page__package { + border-bottom: 2px dashed #CCC; + font-family: Courgette; + font-size: 2em; + line-height: 1.5; + padding-bottom: 0.3em; +} + +.page__package strong { + font-weight: 600; +} + +@media (max-width: 1200px) { + .page { + padding: 40px; + } +} + +@media (max-width: 960px) { + .page { + padding: 20px; + padding-bottom: 40px; + } +} + +.toc { + transition: opacity 320ms; + display: grid; +} + +.toc__wrapper { + border-right: 2px dashed #ccc; + transition: transform 320ms; + box-sizing: border-box; + padding-right: 50px; + margin-right: 50px; +} + +.toc__spacer { + margin-top: 20px; +} + +.toc__page { + text-decoration: none; + font-weight: 600; + display: block; + color: #333; + margin-top: 5px; + /* It's only used for the blog posts. */ +} + +.toc__page.active, +.toc__page:hover { + color: #02b046; +} + +.toc__page div { + align-items: center; + display: flex; +} + +.toc__page svg { + fill: currentColor; + margin-right: 7px; +} + +.toc__page small { + margin-bottom: 15px; + display: block; + color: #aaa; +} + +.toc__pages > .toc__pages { + padding-left: 15px; + margin-left: 5px; + margin-top: 7px; + border-left: 2px dashed #ccc; +} + +.toc__section { + text-transform: uppercase; + font-weight: 600; + font-size: 14px; + color: #999; + margin-bottom: 5px; +} + +.toc__section:not(:first-child) { + margin-top: 30px; +} + +@media (max-width: 1200px) { + .toc { + background: rgba(20, 20, 20, 0.9); + position: fixed; + z-index: 500; + bottom: 0; + right: 0; + left: 0; + top: 0; + pointer-events: none; + opacity: 0; + } + .toc--open { + pointer-events: auto; + opacity: 1; + } + .toc--open .toc__wrapper { + transform: translateX(0); + } + .toc__wrapper { + -webkit-overflow-scrolling: touch; + overflow-x: hidden; + overflow-y: auto; + transform: translateX(-330px); + background: white; + padding: 20px; + width: 280px; + margin: 0; + } +} + +.toc-mobile { + -webkit-touch-callout: none; + display: none; + margin-bottom: 15px; + padding: 10px 15px; + border-radius: 3px; + background: #eee; + cursor: pointer; + font-weight: bold; + justify-content: space-between; + align-items: center; +} + +@media (max-width: 1200px) { + .toc-mobile { + display: flex; + } +} + +.type-badge { + justify-content: center; + display: inline-flex; + align-items: center; + margin-right: 7px; + border-radius: 2px; + font-weight: bold; + font-size: 12px; + height: 20px; + width: 20px; + color: #fff; +} + +.type-badge[data-type="property"] { + background: #23aaaa; +} + +.type-badge[data-type="property"]::before { + content: "P"; +} + +.type-badge[data-type="state"] { + background: orange; +} + +.type-badge[data-type="state"]::before { + content: "S"; +} + +.type-badge[data-type="function"] { + background: black; +} + +.type-badge[data-type="function"]::before { + content: "F"; +} + +.type-badge[data-type="component"] { + background: #369e58; +} + +.type-badge[data-type="component"]::before { + content: "C"; +} + +.type-badge[data-type="provider"] { + background: #ff7b00; +} + +.type-badge[data-type="provider"]::before { + content: "P"; +} + +.type-badge[data-type="record"] { + background: #673ab7; +} + +.type-badge[data-type="record"]::before { + content: "R"; +} + +.type-badge[data-type="module"] { + background: #be08d0; +} + +.type-badge[data-type="module"]::before { + content: "M"; +} + +.type-badge[data-type="store"] { + background: #d02e2e; +} + +.type-badge[data-type="store"]::before { + content: "S"; +} + +.type-badge[data-type="type"] { + background: #00bbb5; +} + +.type-badge[data-type="type"]::before { + content: "T"; +} + +.guide .guide-content h2 a { + color: #333; +} + +.guide-content > .entity__body { + margin: 0; + padding: 0; +} + +.guide .guide-content a:hover, +.entity__type a:hover { + text-decoration: underline; +} + +.entity__signature { + position: relative; +} + +.entity__type.entity__type a { + color: #f77900; +} + +.entity__name + .entity__type::before, +.entity__label + .entity__type::before, +.entity__operator + .entity__type::before { + content: ":"; + color: #aaa; + margin-left: 5px; +} + +.version__dependency a { + color: #02b046; +} + +.entity { + padding: 10px 0; +} + +.entity + a + .entity { + border-top: 1px solid #eee; + padding-top: 30px; + margin-top: 15px; +} + +.entity__body { + padding-top: 20px; + margin-left: 20px; +} + +.entity__body > *:first-child { + margin-top: 0; +} + +.entity__body > *:last-child { + margin-bottom: 0; +} + +.entity__body .language-mint { + max-width: 880px; + font-size: 16px; + line-height: 1.5; + overflow: auto; +} + +@media (max-width: 1000px) { + .entity__body .language-mint { + max-width: calc(100vw - 90px); + } +} + +.entity__argument { + display: flex; +} + +.entity__argument + .entity__argument::before { + margin-right: 10px; + color: #aaa; + content: ","; +} + +.entity__operator { + margin: 0 5px; + color: #aaa; +} + +.entity__operator + .entity__operator { + margin-left: 0; +} + +.entity__type { + color: #f77900; +} + +.entity__name { + text-decoration: none; + font-weight: 600; + color: #29c; +} + +.entity__default { + border: 1px solid #eee; + border-radius: 2px; + background: #f6f6f6; + font-size: 16px; + padding: 10px; + margin: 0; + margin-left: 5px; +} + +.entity__default--block { + font-family: monospace; + display: inline-block; + white-space: pre-wrap; + padding-right: 50px; + margin-left: 30px; + margin-top: 15px; +} + +.entity__signature { + font-family: monospace; + align-items: center; + font-size: 18px; + display: flex; + flex-wrap: wrap; +} + +.entity__type-parameter + .entity__type-parameter::before { + content: ","; + margin-right: 10px; +} + +a.github-source-link.github-source-link { + color: #999; + font-size: 18px; + font-weight: normal; + position: absolute; + right: 0; + top: 0; +} + +a.github-source-link::before { + content: "\003C\002F\003E"; +} + +a.github-source-link:hover { + color: black; +} + +.input-search { + position: relative; +} + +.input-search svg { + position: absolute; + right: 11px; + top: 11px; + fill: #333; +} + +.input-search input { + border: 2px solid #CCC; + padding: 0 10px; + height: 36px; + width: 100%; +} + +.input-search input:focus { + outline: none; + border-color: #02b046; +} + +svg { + max-width: 100%; +} + +pre { + border: 1px dashed currentColor; + background: #FCFCFC; + position: relative; + min-width: 600px; + font-size: 16px; + margin: 0; + outline: 1px solid #CCC; + outline-offset: 3px; +} + +pre code { + background: white; + color: #333; + display: block; + overflow-x: auto; + padding: 15px; +} + +.language-mint .comment { + color: #5c6370; + font-style: italic; +} + +.language-mint .keyword { + color: darkmagenta; +} + +.language-mint .namespace { + color: orangered; +} + +.language-mint .string { + color: seagreen; +} + +.language-mint .type { + color: royalblue; +} + +.language-mint .emphasis { + font-style: italic; +} + +.language-mint .strong { + font-weight: 700; +} + +.language-mint .link { + text-decoration: underline; +} diff --git a/src/assets/docs-html/app.js b/src/assets/docs-html/app.js new file mode 100644 index 000000000..4b6c69cf6 --- /dev/null +++ b/src/assets/docs-html/app.js @@ -0,0 +1,29 @@ +document.getElementById("toc-mobile").addEventListener("click", (e) => { + toc.classList.add("toc--open"); +}); + +document.getElementById("toc").addEventListener("click", (e) => { + toc.classList.remove("toc--open"); +}); + +document.getElementById("toc-wrapper").addEventListener("click", (e) => { + e.stopPropagation(); +}); + +document.getElementById("search").addEventListener("input", (e) => { + const term = e.target.value.toLowerCase().trim(); + + document + .querySelectorAll(`[data-search]`) + .forEach((el) => (el.style.display = "none")); + + if (term === "") { + document + .querySelectorAll(`[data-search-empty="true"]`) + .forEach((el) => (el.style.display = "")); + } else { + document + .querySelectorAll(`[data-search*="${term}"]`) + .forEach((el) => (el.style.display = "")); + } +}); diff --git a/src/assets/docs-html/favicon.png b/src/assets/docs-html/favicon.png new file mode 100644 index 000000000..ea7514eaf Binary files /dev/null and b/src/assets/docs-html/favicon.png differ diff --git a/src/assets/docs-html/fonts.css b/src/assets/docs-html/fonts.css new file mode 100644 index 000000000..8f9383f6f --- /dev/null +++ b/src/assets/docs-html/fonts.css @@ -0,0 +1,72 @@ +/* latin-ext */ +@font-face { + font-family: 'Courgette'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(wEO_EBrAnc9BLjLQAUk1WPoK_kgXiYvO.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Courgette'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(wEO_EBrAnc9BLjLQAUk1VvoK_kgXiQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(o-0NIpQlx3QUlC5A4PNjThZVatyBx2pqPIif.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(o-0NIpQlx3QUlC5A4PNjThZVZNyBx2pqPA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(o-0IIpQlx3QUlC5A4PNr6zRASf6M7VBj.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(o-0IIpQlx3QUlC5A4PNr5TRASf6M7Q.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(o-0NIpQlx3QUlC5A4PNjOhBVatyBx2pqPIif.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(o-0NIpQlx3QUlC5A4PNjOhBVZNyBx2pqPA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr5TRASf6M7Q.woff2 b/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr5TRASf6M7Q.woff2 new file mode 100644 index 000000000..d8786c5b0 Binary files /dev/null and b/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr5TRASf6M7Q.woff2 differ diff --git a/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr6zRASf6M7VBj.woff2 b/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr6zRASf6M7VBj.woff2 new file mode 100644 index 000000000..eb7cb0fa8 Binary files /dev/null and b/src/assets/docs-html/o-0IIpQlx3QUlC5A4PNr6zRASf6M7VBj.woff2 differ diff --git a/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVZNyBx2pqPA.woff2 b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVZNyBx2pqPA.woff2 new file mode 100644 index 000000000..3741eecb2 Binary files /dev/null and b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVZNyBx2pqPA.woff2 differ diff --git a/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVatyBx2pqPIif.woff2 b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVatyBx2pqPIif.woff2 new file mode 100644 index 000000000..21db9f584 Binary files /dev/null and b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjOhBVatyBx2pqPIif.woff2 differ diff --git a/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVZNyBx2pqPA.woff2 b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVZNyBx2pqPA.woff2 new file mode 100644 index 000000000..26e7af841 Binary files /dev/null and b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVZNyBx2pqPA.woff2 differ diff --git a/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVatyBx2pqPIif.woff2 b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVatyBx2pqPIif.woff2 new file mode 100644 index 000000000..f4f6e6cf9 Binary files /dev/null and b/src/assets/docs-html/o-0NIpQlx3QUlC5A4PNjThZVatyBx2pqPIif.woff2 differ diff --git a/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1VvoK_kgXiQ.woff2 b/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1VvoK_kgXiQ.woff2 new file mode 100644 index 000000000..dd59f4285 Binary files /dev/null and b/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1VvoK_kgXiQ.woff2 differ diff --git a/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1WPoK_kgXiYvO.woff2 b/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1WPoK_kgXiYvO.woff2 new file mode 100644 index 000000000..6edcc4b2a Binary files /dev/null and b/src/assets/docs-html/wEO_EBrAnc9BLjLQAUk1WPoK_kgXiYvO.woff2 differ diff --git a/src/commands/docs_generate.cr b/src/commands/docs_generate.cr index 1b1f68384..c6ae7a82c 100644 --- a/src/commands/docs_generate.cr +++ b/src/commands/docs_generate.cr @@ -3,34 +3,68 @@ module Mint class DocsGenerate < Admiral::Command include Command - define_help description: "Starts the documentation server" + define_help description: "Generates static html and json documentation" - define_flag output : String, - description: "The output filename", - default: "docs.json", + define_flag base : String, + description: "Sets the url in the generated html files", + default: "", + short: "b" + + define_flag git_ref : String, + description: "The git reference", required: false, - short: "o" + default: "master", + short: "r" + + define_flag output_dir : String, + description: "The output directory", + default: "docs", + short: "d" + + define_flag json : String, + description: "The json output filename", + default: "docs.json", + short: "j" def run execute "Generating Documentation" do - current = - MintJson.parse_current - ast = Ast.new - current.source_files.each do |file| + mint_json = + MintJson.parse_current + + mint_json.source_files.each do |file| ast.merge(Parser.parse(File.read(file), file)) end ast.normalize - json = - DocumentationGenerator.new.generate(current, ast) + html(ast, mint_json) - File.write(flags.output, json) + json(ast, mint_json) end end + + def html(ast : Ast, mint_json : MintJson) + DocumentationGeneratorHtml.new( + mint_json, + ast, + flags.output_dir, + flags.base, + flags.git_ref + ).generate + end + + def json(ast : Ast, mint_json : MintJson) + json = + DocumentationGeneratorJson.new.generate(mint_json, ast) + + json_file = "#{flags.output_dir}/#{flags.json}" + + Dir.mkdir_p(Path.new(json_file).dirname) + File.write(json_file, json) + end end end end diff --git a/src/compilers/directives/documentation.cr b/src/compilers/directives/documentation.cr index 553ddfd9f..4c9e38d6c 100644 --- a/src/compilers/directives/documentation.cr +++ b/src/compilers/directives/documentation.cr @@ -5,7 +5,7 @@ module Mint lookups[node][0] JSON.build do |json| - DocumentationGenerator.new.generate(entity, json) + DocumentationGeneratorJson.new.generate(entity, json) end end end diff --git a/src/documentation_generator.cr b/src/documentation_generator.cr index 5d65620d0..e5457acee 100644 --- a/src/documentation_generator.cr +++ b/src/documentation_generator.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson include Helpers @formatter = Formatter.new @@ -81,4 +81,237 @@ module Mint @formatter.source(node) end end + + class DocumentationGeneratorHtml + class Page + getter category, subcategory, parent, name + + def initialize(@category : String, @subcategory : String, @parent : String, @name : String) + end + end + + @formatter = Formatter.new + @core_types : Hash(String, String) + @types : Hash(String, String) + @pages = Hash(String, Array(String)).new + @page_children = Hash(Array(String), Array(String)).new + @category : String = "" + @page : String = "" + @git_root : String = "" + + def initialize(@mint_json : MintJson, @ast : Ast, @output_dir : String, @base : String, @git_ref : String) + @git_root = parse_git_root + @pages = { + "components" => @ast.components.map(&.name.value).sort!, + "modules" => @ast.modules.map(&.name.value).sort!, + "providers" => @ast.providers.map(&.name.value).sort!, + "stores" => @ast.stores.map(&.name.value).sort!, + "types" => @ast.type_definitions.map(&.name.value).sort!, + } + @core_types = build_types_lookup(Core.ast) + @types = build_types_lookup(ast) + end + + def build_types_lookup(a : Ast) + # order matters here when things get dedeuped + all = a.components.map { |i| [i.name.value, "components"] } | + a.modules.map { |i| [i.name.value, "modules"] } | + a.providers.map { |i| [i.name.value, "providers"] } | + a.stores.map { |i| [i.name.value, "stores"] } | + a.type_definitions.map { |i| [i.name.value, "types"] } + + all.to_h + end + + def generate + Dir.mkdir_p("#{@output_dir}") + Dir.mkdir_p("#{@output_dir}/assets") + write_assets() + write_readme() + write_pages() + end + + def write_assets + Assets.files + .select(&.path.includes?("/docs-html/")) + .each { |file| + content = file.gets_to_end + basename = Path[file.path].basename + path = Path[@output_dir, "assets", basename].to_s + File.write(path, content) + } + end + + def write_pages + @ast.components.each { |node| write_page("components", node) } + @ast.type_definitions.each { |node| write_page("types", node) } + @ast.modules.each { |node| write_page("modules", node) } + @ast.providers.each { |node| write_page("providers", node) } + @ast.stores.each { |node| write_page("stores", node) } + end + + def write_page(category : String, node : Ast::Node) + @category = category + @page = node.name.value + + # ameba:disable Lint/UselessAssign + content = generate(node) + + html = render("#{__DIR__}/documentation_generator/html/page.ecr") + + Dir.mkdir_p("#{@output_dir}/#{@category}") + File.write("#{@output_dir}/#{@category}/#{@page}.html", html) + end + + def write_readme + @page = "README" + + # ameba:disable Lint/UselessAssign + content = read_markdown("README.md") + + html = render("#{__DIR__}/documentation_generator/html/page.ecr") + + File.write("#{@output_dir}/index.html", html) + end + + def nav_item(node : Ast::Node, category : String) + render("#{__DIR__}/documentation_generator/html/nav_item.ecr") + end + + def generate(node) + end + + def stringify(children : Array(Page)) + children.map(&.name).join("|") + end + + def stringify(nodes : Array(Ast::Node)) + nodes.join(", ") { |node| stringify node } + end + + def stringify(node) + "" + end + + def read_markdown(path : String) + content = + begin + File.read("#{@mint_json.root}/#{path}") + rescue + "# Could not find a #{path} file" + end + + markdown = Markd.to_html(content) + + highlight_markdown(markdown) + end + + def highlight_markdown(markdown : String) + markdown.gsub(/((.|\n)*)<\/code>/) { + html = HTML.unescape($1) + + tokenized = SemanticTokenizer.highlight(html, "example.mint", true) + + "#{tokenized}" + } + end + + def source(node : Ast::Node) : String + @formatter.source(node) + end + + def source(node : Ast::Node | Nil) : String + "" + end + + def search(node) + "#{stringify(node)}|#{stringify(children(node))}".downcase + end + + def children(category : String, subcategory : String, page : Ast::Node, children : Array(Ast::Node)) : Array(Page) + children + .sort_by(&.name.value) + .map { |node| Page.new(category, subcategory, page.name.value, node.name.value) } + end + + def children(node) + [] of Page + end + + def is_page_active(category : String, node : Ast::Node) + category == @category && stringify(node) == @page + end + + def is_git_repo + `git rev-parse --is-inside-work-tree`.strip == "true" + end + + def parse_git_root : String + if is_git_repo + `git rev-parse --show-toplevel`.strip + else + "" + end + end + + def base_url + if @base == "" + "/" + else + "" + end + end + + def readme_url + "#{base_url}index.html" + end + + def page_url(category : String, page : String) + "#{base_url}#{category}/#{page}.html" + end + + def anchor_url(node) + "#{page_url(@category, @page)}##{stringify(node)}" + end + + def anchor_url(child : Page) + "#{page_url(child.category, child.parent)}##{child.name}" + end + + def type_url(type : String) + core = @core_types.fetch(type, "") + own = @types.fetch(type, "") + + if own != "" + page_url(own, type) + elsif core != "" + "https://mint-lang.com/api/#{core}/#{type}" + else + "" + end + end + + def source_url(node) + url = @mint_json.source_url + user, repo = SourceUrl.parse_user_and_repo(url) + path = node.location.filename.sub("#{@git_root}/", "") + line = node.location.start[0] + s_url = + if url.includes?("github") + "https://github.com/#{user}/#{repo}/blob/#{@git_ref}/#{path}#L#{line}" + elsif url.includes?("gitlab") + "https://gitlab.com/#{user}/#{repo}/blob/#{@git_ref}/#{path}#L#{line}" + elsif url.includes?("bitbucket") + "https://bitbucket.org/#{user}/#{repo}/src/#{@git_ref}/#{path}#cl-#{line}" + else + "" + end + + if s_url.empty? + "" + else + "" + end + end + end end diff --git a/src/documentation_generator/argument.cr b/src/documentation_generator/argument.cr index 3a9f627b3..428e84344 100644 --- a/src/documentation_generator/argument.cr +++ b/src/documentation_generator/argument.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Argument, json : JSON::Builder) json.object do json.field "type", stringify(node.type) @@ -7,4 +7,14 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Argument) + render("#{__DIR__}/html/argument.ecr") + end + + def stringify(node : Ast::Argument) + node.name.value + end + end end diff --git a/src/documentation_generator/component.cr b/src/documentation_generator/component.cr index a083721a6..cf0a5d44b 100644 --- a/src/documentation_generator/component.cr +++ b/src/documentation_generator/component.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Component, json : JSON::Builder) json.object do json.field "description", node.comment.try(&.to_html) @@ -33,4 +33,24 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Component) + render("#{__DIR__}/html/component.ecr") + end + + def stringify(node : Ast::Component) + node.name.value + end + + def children(node : Ast::Component) + children("components", "property", node, node.properties) | + children("components", "state", node, node.states) | + children("components", "function", node, node.functions) + end + + def comment(node : Ast::Node) + render("#{__DIR__}/html/comment.ecr") + end + end end diff --git a/src/documentation_generator/connect.cr b/src/documentation_generator/connect.cr index e949bbc57..968ce73d9 100644 --- a/src/documentation_generator/connect.cr +++ b/src/documentation_generator/connect.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Connect, json : JSON::Builder) json.object do json.field "keys", node.keys.map(&.name.value) diff --git a/src/documentation_generator/function.cr b/src/documentation_generator/function.cr index 96228e752..45ee06184 100644 --- a/src/documentation_generator/function.cr +++ b/src/documentation_generator/function.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Function, json : JSON::Builder) json.object do json.field "type", node.type.try { |item| stringify(item) } @@ -13,4 +13,18 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Function) + render("#{__DIR__}/html/function.ecr") + end + + def stringify(node : Ast::Function) + node.name.value + end + + def comment(node : Ast::Function) + render("#{__DIR__}/html/comment.ecr") + end + end end diff --git a/src/documentation_generator/get.cr b/src/documentation_generator/get.cr index 6ee132e12..44abc9ed7 100644 --- a/src/documentation_generator/get.cr +++ b/src/documentation_generator/get.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Get, json : JSON::Builder) json.object do json.field "type", node.type.try { |item| stringify(item) } diff --git a/src/documentation_generator/html/argument.ecr b/src/documentation_generator/html/argument.ecr new file mode 100644 index 000000000..eb3283de5 --- /dev/null +++ b/src/documentation_generator/html/argument.ecr @@ -0,0 +1,7 @@ +
+
+ <%= stringify(node) %> +
+ + <%= generate(node.type) %> +
\ No newline at end of file diff --git a/src/documentation_generator/html/comment.ecr b/src/documentation_generator/html/comment.ecr new file mode 100644 index 000000000..2d183db87 --- /dev/null +++ b/src/documentation_generator/html/comment.ecr @@ -0,0 +1,5 @@ +<% if node.comment != nil %> +
+ <%= node.comment.try(&.to_html) %> +
+<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/component.ecr b/src/documentation_generator/html/component.ecr new file mode 100644 index 000000000..7c5a421e0 --- /dev/null +++ b/src/documentation_generator/html/component.ecr @@ -0,0 +1,37 @@ +

+ <%= stringify(node) %> +
+ <%= source_url(node) %> +

+ +<%= comment(node) %> + +<% if node.properties.size > 0 %> +

+ Properties +

+ + <% node.properties.sort_by(&.name.value).each do |p| %> + <%= generate(p) %> + <% end %> +<% end %> + +<% if node.states.size > 0 %> +

+ States +

+ + <% node.states.sort_by(&.name.value).each do |s| %> + <%= generate(s) %> + <% end %> +<% end %> + +<% if node.functions.size > 0 %> +

+ Functions +

+ + <% node.functions.sort_by(&.name.value).each do |f| %> + <%= generate(f) %> + <% end %> +<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/default.ecr b/src/documentation_generator/html/default.ecr new file mode 100644 index 000000000..49cb41582 --- /dev/null +++ b/src/documentation_generator/html/default.ecr @@ -0,0 +1,13 @@ +<% default = source(node.default) %> + +<% if default != "" %> + <% block = default.size > 50 ? "entity__default--block" : "" %> + +
+ = +
+ +
+ <%= default %> +
+<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/function.ecr b/src/documentation_generator/html/function.ecr new file mode 100644 index 000000000..7c5cab08b --- /dev/null +++ b/src/documentation_generator/html/function.ecr @@ -0,0 +1,26 @@ + + +
+
+ +
<%= stringify(node) %>
+
+ + <% if node.arguments.try(&.to_a.any?) %> +
(
+ + <% node.arguments.each do |a| %> + <%= generate(a) %> + <% end %> + +
)
+ <% end %> + + <%= generate(node.type) %> + + <%= source_url(node) %> +
+ + + <%= comment(node) %> +
\ No newline at end of file diff --git a/src/documentation_generator/html/module.ecr b/src/documentation_generator/html/module.ecr new file mode 100644 index 000000000..1a4e0123a --- /dev/null +++ b/src/documentation_generator/html/module.ecr @@ -0,0 +1,13 @@ +

+ <%= stringify(node) %> +
+ <%= source_url(node) %> +

+ +<%= comment(node) %> + +

Functions

+ +<% node.functions.sort_by(&.name.value).each do |f| %> + <%= generate(f) %> +<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/nav_item.ecr b/src/documentation_generator/html/nav_item.ecr new file mode 100644 index 000000000..4b0aa6009 --- /dev/null +++ b/src/documentation_generator/html/nav_item.ecr @@ -0,0 +1,30 @@ + +
+ + <%= stringify(node) %> + +
+ +<% children(node).each do |child| %> + + <% if !is_page_active(category, node) %> + style="display: none" + <% end %> +> +    +
+ + <%= child.@name %> + +
+<% end %> diff --git a/src/documentation_generator/html/page.ecr b/src/documentation_generator/html/page.ecr new file mode 100644 index 000000000..9d5361e06 --- /dev/null +++ b/src/documentation_generator/html/page.ecr @@ -0,0 +1,160 @@ + + + + <% if @base != "" %> + + <% end %> + + <%= @mint_json.name %> | <%= @page %> + + + + + + +
+
+
+
+ Pages + +
+
+
+
+ <%= @mint_json.name %> +
+ +
Basic information
+ +
+ +
+ + README +
+
+ + <% if !@mint_json.source_url.empty? %> + +
+ + Source on GitHub +
+
+ <% end %> +
+ +
Entities
+ + + + + +
Dependencies
+ +
+ <% if @mint_json.dependencies.each == 0 %> +
+ No Dependencies +
+ <% else %> + <% @mint_json.dependencies.each do |d|%> +
+ + <%= d.name %> + + <%= d.constraint.to_s %> +
+ <% end %> + <% end %> +
+
+
+ +
+ <%= content %> +
+
+
+
+ + + diff --git a/src/documentation_generator/html/property.ecr b/src/documentation_generator/html/property.ecr new file mode 100644 index 000000000..1f525e387 --- /dev/null +++ b/src/documentation_generator/html/property.ecr @@ -0,0 +1,17 @@ + + +
+
+ +
<%= stringify(node) %>
+
+ + <%= generate(node.type) %> + + <%= default(node) %> + + <%= source_url(node) %> +
+ + <%= comment(node) %> +
\ No newline at end of file diff --git a/src/documentation_generator/html/provider.ecr b/src/documentation_generator/html/provider.ecr new file mode 100644 index 000000000..22d27ae75 --- /dev/null +++ b/src/documentation_generator/html/provider.ecr @@ -0,0 +1,13 @@ +

+ <%= stringify(node) %> +
+ <%= source_url(node) %> +

+ +<%= comment(node) %> + +

Functions

+ +<% node.functions.sort_by(&.name.value).each do |f| %> + <%= generate(f) %> +<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/state.ecr b/src/documentation_generator/html/state.ecr new file mode 100644 index 000000000..1f525e387 --- /dev/null +++ b/src/documentation_generator/html/state.ecr @@ -0,0 +1,17 @@ + + +
+
+ +
<%= stringify(node) %>
+
+ + <%= generate(node.type) %> + + <%= default(node) %> + + <%= source_url(node) %> +
+ + <%= comment(node) %> +
\ No newline at end of file diff --git a/src/documentation_generator/html/store.ecr b/src/documentation_generator/html/store.ecr new file mode 100644 index 000000000..af0b40b6d --- /dev/null +++ b/src/documentation_generator/html/store.ecr @@ -0,0 +1,16 @@ +

+ <%= stringify(node) %> +
+

+ +<%= comment(node) %> + +

States

+ +<% node.states.sort_by(&.name.value).each do |s| %> + <%= generate(s) %> +<% end %> + +<% node.functions.sort_by(&.name.value).each do |s| %> + <%= generate(s) %> +<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/type.ecr b/src/documentation_generator/html/type.ecr new file mode 100644 index 000000000..41f2d1de9 --- /dev/null +++ b/src/documentation_generator/html/type.ecr @@ -0,0 +1,3 @@ +
+ <%= stringify(node) %> +
\ No newline at end of file diff --git a/src/documentation_generator/html/type_definition.ecr b/src/documentation_generator/html/type_definition.ecr new file mode 100644 index 000000000..79664f312 --- /dev/null +++ b/src/documentation_generator/html/type_definition.ecr @@ -0,0 +1,11 @@ +

+ <%= stringify(node) %> +
+ <%= source_url(node) %> +

+ +

Fields

+ +<% node.fields.sort_by{|f| stringify(f) }.each do |f| %> + <%= generate(f) %> +<% end %> \ No newline at end of file diff --git a/src/documentation_generator/html/type_definition_field.ecr b/src/documentation_generator/html/type_definition_field.ecr new file mode 100644 index 000000000..79da2eb06 --- /dev/null +++ b/src/documentation_generator/html/type_definition_field.ecr @@ -0,0 +1,13 @@ + + +
+
+ +
<%= node.key.value %>
+
+ + <%= generate(node.type) %> + + <%= source_url(node) %> +
+
\ No newline at end of file diff --git a/src/documentation_generator/html/type_variable.ecr b/src/documentation_generator/html/type_variable.ecr new file mode 100644 index 000000000..5f39a4504 --- /dev/null +++ b/src/documentation_generator/html/type_variable.ecr @@ -0,0 +1,5 @@ + + + <%= stringify(node) %> + + \ No newline at end of file diff --git a/src/documentation_generator/html/type_variant.ecr b/src/documentation_generator/html/type_variant.ecr new file mode 100644 index 000000000..78362f787 --- /dev/null +++ b/src/documentation_generator/html/type_variant.ecr @@ -0,0 +1,21 @@ +
+
+ + <%= stringify(node) %> + + + <% if node.parameters.to_a.any? %> + ( + + <% node.parameters.each do |p| %> + <%= generate(p) %> + <% end %> + + ) + <% end %> + + <%= source_url(node) %> +
+ + <%= comment(node) %> +
\ No newline at end of file diff --git a/src/documentation_generator/id.cr b/src/documentation_generator/id.cr index 6f89f9405..31c246ab3 100644 --- a/src/documentation_generator/id.cr +++ b/src/documentation_generator/id.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def stringify(node : Ast::Id) node.value end @@ -8,4 +8,16 @@ module Mint json.string stringify(node) end end + + class DocumentationGeneratorHtml + def stringify(node : Ast::Id) + url = type_url(node.value) + + if url != "" + "#{node.value}" + else + node.value + end + end + end end diff --git a/src/documentation_generator/module.cr b/src/documentation_generator/module.cr index ab03c122b..6263b2406 100644 --- a/src/documentation_generator/module.cr +++ b/src/documentation_generator/module.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Module, json : JSON::Builder) json.object do json.field "description", node.comment.try(&.to_html) @@ -13,4 +13,18 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Module) + render("#{__DIR__}/html/module.ecr") + end + + def stringify(node : Ast::Module) + node.name.value + end + + def children(node : Ast::Module) + children("modules", "function", node, node.functions) + end + end end diff --git a/src/documentation_generator/property.cr b/src/documentation_generator/property.cr index 30ade7faa..dd35ee819 100644 --- a/src/documentation_generator/property.cr +++ b/src/documentation_generator/property.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Property, json : JSON::Builder) json.object do json.field "default", node.default.try { |item| source(item) } @@ -9,4 +9,22 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Property) + render("#{__DIR__}/html/property.ecr") + end + + def stringify(node : Ast::Property) + node.name.value + end + + def default(node : Ast::Property) + render("#{__DIR__}/html/default.ecr") + end + + def comment(node : Ast::Property) + render("#{__DIR__}/html/comment.ecr") + end + end end diff --git a/src/documentation_generator/provider.cr b/src/documentation_generator/provider.cr index 8b6a74f0c..3b092ba84 100644 --- a/src/documentation_generator/provider.cr +++ b/src/documentation_generator/provider.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Provider, json : JSON::Builder) json.object do json.field "description", node.comment.try(&.to_html) @@ -14,4 +14,18 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Provider) + render("#{__DIR__}/html/provider.ecr") + end + + def stringify(node : Ast::Provider) + node.name.value + end + + def children(node : Ast::Provider) + children("providers", "function", node, node.functions) + end + end end diff --git a/src/documentation_generator/state.cr b/src/documentation_generator/state.cr index 2862337dc..4a498eb03 100644 --- a/src/documentation_generator/state.cr +++ b/src/documentation_generator/state.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::State, json : JSON::Builder) json.object do json.field "type", node.type.try { |item| stringify(item) } @@ -9,4 +9,22 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::State) + render("#{__DIR__}/html/state.ecr") + end + + def stringify(node : Ast::State) + node.name.value + end + + def default(node : Ast::State) + render("#{__DIR__}/html/default.ecr") + end + + def comment(node : Ast::State) + render("#{__DIR__}/html/comment.ecr") + end + end end diff --git a/src/documentation_generator/store.cr b/src/documentation_generator/store.cr index 01ab973bd..82284e537 100644 --- a/src/documentation_generator/store.cr +++ b/src/documentation_generator/store.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Store, json : JSON::Builder) json.object do json.field "name" do @@ -22,4 +22,19 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::Store) + render("#{__DIR__}/html/store.ecr") + end + + def stringify(node : Ast::Store) + node.name.value + end + + def children(node : Ast::Store) + children("stores", "state", node, node.states) | + children("stores", "function", node, node.functions) + end + end end diff --git a/src/documentation_generator/type.cr b/src/documentation_generator/type.cr index da466520a..39bd0713c 100644 --- a/src/documentation_generator/type.cr +++ b/src/documentation_generator/type.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def stringify(node : Ast::Type) parameters = unless node.parameters.empty? @@ -13,4 +13,23 @@ module Mint json.string stringify(node) end end + + class DocumentationGeneratorHtml + def stringify(node : Ast::Type) + parameters = + unless node.parameters.empty? + "(#{stringify(node.parameters)})" + end + + "#{stringify node.name}#{parameters}" + end + + def generate(node : Ast::Type | Nil) + if node + render("#{__DIR__}/html/type.ecr") + else + "" + end + end + end end diff --git a/src/documentation_generator/type_definition.cr b/src/documentation_generator/type_definition.cr index 585704538..f130674cb 100644 --- a/src/documentation_generator/type_definition.cr +++ b/src/documentation_generator/type_definition.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::TypeDefinition, json : JSON::Builder) json.object do json.field "description", node.comment.try(&.to_html) @@ -13,4 +13,14 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::TypeDefinition) + render("#{__DIR__}/html/type_definition.ecr") + end + + def stringify(node : Ast::TypeDefinition) + node.name.value + end + end end diff --git a/src/documentation_generator/type_definition_field.cr b/src/documentation_generator/type_definition_field.cr index a7daa1bb7..3d92fab29 100644 --- a/src/documentation_generator/type_definition_field.cr +++ b/src/documentation_generator/type_definition_field.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::TypeDefinitionField, json : JSON::Builder) json.object do json.field "key", node.key.value @@ -8,4 +8,14 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::TypeDefinitionField) + render("#{__DIR__}/html/type_definition_field.ecr") + end + + def stringify(node : Ast::TypeDefinitionField) + node.key.value + end + end end diff --git a/src/documentation_generator/type_variable.cr b/src/documentation_generator/type_variable.cr index 61f115780..93c599a64 100644 --- a/src/documentation_generator/type_variable.cr +++ b/src/documentation_generator/type_variable.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def stringify(node : Ast::TypeVariable) node.value end @@ -8,4 +8,14 @@ module Mint json.string node.value end end + + class DocumentationGeneratorHtml + def generate(node : Ast::TypeVariable) + render("#{__DIR__}/html/type_variable.ecr") + end + + def stringify(node : Ast::TypeVariable) + node.value + end + end end diff --git a/src/documentation_generator/type_variant.cr b/src/documentation_generator/type_variant.cr index eb40c1317..e9b1dfabb 100644 --- a/src/documentation_generator/type_variant.cr +++ b/src/documentation_generator/type_variant.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::TypeVariant, json : JSON::Builder) json.object do json.field "description", node.comment.try(&.to_html) @@ -13,4 +13,18 @@ module Mint end end end + + class DocumentationGeneratorHtml + def generate(node : Ast::TypeVariant) + render("#{__DIR__}/html/type_variant.ecr") + end + + def comment(node : Ast::TypeVariant) + render("#{__DIR__}/html/comment.ecr") + end + + def stringify(node : Ast::TypeVariant) + node.value.value + end + end end diff --git a/src/documentation_generator/use.cr b/src/documentation_generator/use.cr index c5bcfbdaf..5fa7d29f1 100644 --- a/src/documentation_generator/use.cr +++ b/src/documentation_generator/use.cr @@ -1,5 +1,5 @@ module Mint - class DocumentationGenerator + class DocumentationGeneratorJson def generate(node : Ast::Use, json : JSON::Builder) condition = node.condition.try do |expression| diff --git a/src/documentation_server.cr b/src/documentation_server.cr index 740c855f7..3f2f7704f 100644 --- a/src/documentation_server.cr +++ b/src/documentation_server.cr @@ -3,7 +3,7 @@ module Mint @asts = {} of MintJson => Ast @error : String? @ast = Ast.new - @generator = DocumentationGenerator.new + @generator = DocumentationGeneratorJson.new def self.start new diff --git a/src/mint_json.cr b/src/mint_json.cr index e14bc325d..1fdaa23ed 100644 --- a/src/mint_json.cr +++ b/src/mint_json.cr @@ -31,6 +31,7 @@ module Mint getter application = Application.new getter root : String getter name = "" + getter source_url = "" def initialize(@json : String, @root : String, @file : String) begin @@ -150,10 +151,12 @@ module Mint parse_external_assets when "web-components" parse_web_components + when "source-url" + parse_source_url else error! :mint_json_root_invalid_key do block do - text "The root object of a" + text "The root object in the" bold "mint.json" text "file has an invalid key:" bold key @@ -189,7 +192,7 @@ module Mint block do text "The" bold "name" - text "field of a" + text "field in the" bold "mint.json" text "file is empty:" end @@ -201,7 +204,7 @@ module Mint block do text "The" bold "name" - text "field of a" + text "field in the" bold "mint.json" text "file is not a string." end @@ -224,7 +227,7 @@ module Mint block do text "The" bold "mint-version" - text "field in your" + text "field in the" bold "mint.json" text "file is empty." end @@ -268,7 +271,7 @@ module Mint block do text "The" bold "mint-version" - text "field in your" + text "field in the" bold "mint.json" text "file does not match your current version of Mint." end @@ -289,7 +292,7 @@ module Mint block do text "The" bold "mint-version" - text "field in your" + text "field in the" bold "mint.json" text "file is not a string." end @@ -690,7 +693,7 @@ module Mint block do text "The" bold "formatter-config" - text "object of a" + text "object in the" bold "mint.json" text "file has an invalid key:" bold key @@ -793,7 +796,7 @@ module Mint block do text "The" bold "application object" - text "of a" + text "in the" bold "mint.json" text "file has an invalid key:" bold key @@ -1176,6 +1179,29 @@ module Mint end end + def parse_source_url + location = + @parser.location + + @source_url = + @parser.read_string + + user, repo = SourceUrl.parse_user_and_repo(@source_url) + + error! :mint_json_source_url_invalid do + block do + text "The" + bold "source-url" + text "field in the" + bold "mint.json" + text "has an invalid format, must be" + bold "https://domain/user/repo:" + end + + snippet node(location) + end if user.empty? || repo.empty? + end + def check_dependencies! dependencies.each do |dependency| next if dependency_exists?(dependency.name) diff --git a/src/semantic_tokenizer.cr b/src/semantic_tokenizer.cr index 96e5eee6a..98e159738 100644 --- a/src/semantic_tokenizer.cr +++ b/src/semantic_tokenizer.cr @@ -67,9 +67,18 @@ module Mint end def self.highlight(path : String, html : Bool = false) - ast = - Parser.parse(path) + ast = Parser.parse(path) + highlight(ast, html) + end + + def self.highlight(text : String, file : String, html : Bool = false) + ast = Parser.parse(text, file) + + highlight(ast, html) + end + + def self.highlight(ast : Ast, html : Bool = false) parts = tokenize(ast) diff --git a/src/utils/source_url.cr b/src/utils/source_url.cr new file mode 100644 index 000000000..7a0a533c3 --- /dev/null +++ b/src/utils/source_url.cr @@ -0,0 +1,11 @@ +module Mint + class SourceUrl + def self.parse_user_and_repo(url : String) : Tuple(String, String) + if /[https?:\/\/]?(.*)\.(.*)\/(.*)\/(.*)\/?/.match(url) + {$3, $4} + else + {"", ""} + end + end + end +end