Permalink
Browse files

First commit, post-squash

  • Loading branch information...
Li Haoyi
Li Haoyi committed Jan 7, 2016
0 parents commit bd4a7ebcece2f8168d9f507a3b626169f22938c1
Showing with 428 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +89 −0 build.scala
  3. +149 −0 pages.scala
  4. +96 −0 posts/0 - Hello World Blog.md
  5. +84 −0 styles.scala
@@ -0,0 +1,10 @@
target/
*.iml
.idea
.settings
.classpath
.project
.cache
.sbtserver
project/.sbtserver
tags
@@ -0,0 +1,89 @@
// Load dependencies
load.ivy("org.pegdown" % "pegdown" % "1.6.0")
load.ivy("com.lihaoyi" %% "scalatags" % "0.5.3")
load.module(ammonite.ops.cwd/"styles.scala")
load.module(ammonite.ops.cwd/"pages.scala")
@
import scalatags.Text.all.{width, height, _}
import scalatags.Text._
import ammonite.ops._
import org.pegdown.{PegDownProcessor, ToHtmlSerializer, LinkRenderer}
val postsFolder = cwd/'posts
val targetFolder = cwd/'target
object DatesFor{
import ammonite.ops.ImplicitWd._
val commitChunks = %%('git, 'log, "--date=short").out.string.split("\n(?=commit)")
val commits = for(chunk <- commitChunks.dropRight(1)) yield {
val lines = chunk.lines.toSeq
val sha = lines(0).stripPrefix("commit ")
val author = lines(1).stripPrefix("Author: ")
val date = lines(2).stripPrefix("Date: ")
val files = %%('git, 'diff, "--name-only", sha, sha + "~1").out.lines
(sha, author, date, files)
}
val fileChanges = for{
(sha, author, date, files) <- commits
file <- files
} yield (file, sha, author, date)
def apply(filePrefix: String) = for {
(file, sha, author, date) <- fileChanges
if file.startsWith(filePrefix)
} yield (sha, java.time.LocalDate.parse(date))
}
// Walk the posts/ folder and parse out the name, full- and first-paragraph-
// HTML of each post to be used on their respective pages and on the index
val posts = {
val split = for(path <- ls.rec! postsFolder if path.ext == "md") yield {
val Array(number, name) = path.last.split(" - ")
(number, name.stripSuffix(".md"), path)
}
for ((index, name, path) <- split.sortBy(_._1.toInt)) yield {
val processor = new PegDownProcessor()
val ast = processor.parseMarkdown(read! path toArray)
val rawHtmlContent = new ToHtmlSerializer(new LinkRenderer).toHtml(ast)
if (ast.getChildren.size > 0) {
val firstNode = ast.getChildren.get(0)
ast.getChildren.clear()
ast.getChildren.add(firstNode)
}
val rawHtmlSnippet = new ToHtmlSerializer(new LinkRenderer).toHtml(ast)
val updates = DatesFor(s"posts/$index - ").toSeq
(name, rawHtmlContent, rawHtmlSnippet, updates)
}
}
def main(publish: Boolean = false) = {
rm! targetFolder
write(
targetFolder/s"index.html",
mainContent(posts)
)
for((name, rawHtmlContent, _, dates) <- posts){
write(
targetFolder/'post/s"$name.html",
postContent(name, rawHtmlContent, dates)
)
}
if (publish){
implicit val publishWd = cwd/'target
%git 'init
%git('add, "-A", ".")
%git('commit, "-am", "first commit")
%git('remote, 'add, 'origin, "git@github.com:lihaoyi/lihaoyi.github.io.git")
%git('push, "-uf", 'origin, 'master)
}
}
@@ -0,0 +1,149 @@
load.ivy("com.lihaoyi" %% "scalatags" % "0.5.3")
load.module(ammonite.ops.cwd/"styles.scala")
@
import scalatags.Text.all.{width, height, _}
import scalatags.Text._
import java.time.LocalDate
@
def pageChrome(titleText: Option[String], unNesting: String, contents: Frag): String = {
val sheets = Seq(
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
"https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"
)
val headerLinks = Seq(
div(div(i(cls:= "fa fa-question-circle")), " About") -> s"$unNesting/post/Hello%20World%20Blog.html",
div(div(i(cls:= "fa fa-file-text-o")), " Resume") -> "https://lihaoyi.github.io/Resume/",
div(div(i(cls:= "fa fa-github")), " Github") -> "https://github.com/lihaoyi"
)
html(
head(
meta(charset := "utf-8"),
for(sheet <- sheets)
yield link(href := sheet, rel := "stylesheet", `type` := "text/css" ),
tags2.title("lihaoyi.com: " + titleText),
tags2.style(s"@media (min-width: 48em) {${WideStyles.styleSheetText}}"),
tags2.style(s"@media (max-width: 48em) {${NarrowStyles.styleSheetText}}"),
tags2.style(Styles.styleSheetText)
),
body(
margin := 0,
div(
WideStyles.header,
NarrowStyles.header,
Styles.header,
div(
NarrowStyles.headerContent,
WideStyles.headerContent,
h1(
a(
i(cls:= "fa fa-cogs"),
color := "white",
" Haoyi's Programming Blog", href := s"$unNesting/index.html",
Styles.subtleLink,
fontWeight.bold
),
padding := "10px 10px",
margin := 0
),
div(
Styles.headerLinkBox,
NarrowStyles.linkFlex,
for ((name, url) <- headerLinks) yield div(
Styles.headerLink,
a(name, href := url, Styles.subtleLink, color := "white")
)
)
)
),
div(
WideStyles.content,
NarrowStyles.content,
maxWidth := 900,
titleText.map(h1(_)),
contents
),
div(
WideStyles.footer,
Styles.footer,
"Last published ", currentTimeText
)
)
).render
}
val currentTimeText = LocalDate.now.toString
def commentBox(titleText: String): Frag = Seq(
div(id:="disqus_thread"),
script(raw(s"""
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables
*/
/*
var disqus_config = function () {
this.page.url = "https://www.lihaoyi.com/p/$titleText"; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = "$titleText"; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = '//lihaoyi.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
"""))
)
def metadata(dates: Seq[(String, LocalDate)]) = div(
color := "#999",
marginBottom := 20,
"Posted ",
for ((sha, date) <- dates.headOption) yield a(
date.toString, href := s"https://github.com/lihaoyi/site/commit/$sha"
)
)
def mainContent(posts: Seq[(String, String, String, Seq[(String, LocalDate)])]) = pageChrome(
None,
".",
div(
for((name, _, rawHtmlSnippet, dates) <- posts.reverse) yield div(
h1(a(
name,
href := s"post/$name.html",
Styles.subtleLink,
color := "rgb(34, 34, 34)"
)),
metadata(dates),
raw(rawHtmlSnippet),
hr(margin := "50px 0px 50px 0px")
)
)
)
def postContent(name: String, rawHtmlContent: String, dates: Seq[(String, LocalDate)]) = pageChrome(
Some(name),
"..",
Seq[Frag](
metadata(dates),
raw(rawHtmlContent),
if (dates.length < 2) ""
else {
div(
hr,
div(
color := "rgb(158, 167, 174)",
"Updated ",
for((sha, date) <- dates.drop(1)) yield a(
date.toString, " ", href := s"https://github.com/lihaoyi/site/commit/$sha"
)
)
)
},
commentBox(name)
)
)
@@ -0,0 +1,96 @@
I have a new programming blog, and you're looking at it! I'll post things
about programming on it and there'll be a comment box at the bottom of each
post if anyone wants to discuss the things I posted. I'm currently working with
the Scala, Python and Javascript programming languages, and am interested in
static analysis, compilers, web development and developer tools.
I am not the first person to have a blog, nor the first person to have a
programming blog, nor is this my first blog. Nevertheless, for me, doing
this is a mix of new and old ideas.
## The Old
This is not the first time I have had a blog/site. I had built one back
in 2011 while in college, and it was very much my own introduction to
web development and distributed systems. The [source code][0] is online, in
all its gory details: it's custom templating language, it's now-defunct
Facebook API keys, random server logs, and other things. The content is
missing, because I misplaced it, and who used version control back then
anyway? Some can be retrieved from the [Web Archive], while others
(like all the static assets and images) seems lost forever
This is not the first time I have published things on the internet. I've
written hundreds of pages of documentation for the open source projects I've
been involved in [1] [2] [3], including the [Hands-on Scala.js] e-book.
Nor is this the first time I've had discussions with other people online.
I'm relatively active on Twitter, on the mailing lists, on the programming
reddits, and on the Gitter channels of various projects.
## The New
Given that all these things have already happened, what then is the value
of starting a blog, yet *another* place to write things, for people to look
at, and all that?
### Audience
Unlike the 2011 blog, this one actually has a target audience.
The original was a somewhat meandering smattering of "things I thought were
cool": programming, photography, DIY pneumatics and electronics. Although I
don't have the content anymore, none of it was all that interesting to begin
with, and given how un-targeted it was it's unlikely a random passer-by would
find it interesting. In contrast this one is a lot more targeted: I will talk
about programming, and presumably the people coming here will be interested in
programming, and everyone will find something they're interested it. As for
my other interests, they may start appearing here eventually, or I may simple
continue publishing those on other channels like Facebook or mailing-lists.
### Lifetimes
This blog is meant to bridge a gap between the forever-lived documentation
sites I publish for projects and short-lived emails, tweets and chat messages.
A chat message (e.g. on Gitter) takes seconds to compose and lives for minutes
before being buried. An email or tweet may takes minutes to write and live for
days, but they soon get buried too (The entire thread of exchange may last
longer for weeks, but the individual messages get buried quickly and disappear
from the public eye).
At the other extreme are the documentation sites: things like the documentation
for [Ammonite][1] or [FastParse][3] are built up over multiple days or weeks,
and I expect them to last forever. That slows things down, as I have to make
sure new additions "fit in" to the existing docs, and need to be careful to
keep things in sync and ensure the docs don't get broken.
The posts on this blog are expected to take hours to a day compose and live for
months to a year.
While a doc-site has to be maintained and kept updated as the thing it's
documenting changes, the content here will be in plain markdown. It will be
neither maintainable nor maintained, will not be well organized, and will
definitely become stale over time. This freedom from maintenance to write more
and more varied things that I'd be able to fit into a doc-site'.
Unlike channels like Twitter or Gitter or Mailing lists, the content here
will be longer-form and longer-lived. The short time on chat or social-media
limit how in-depth you can discuss things: there simply isn't enough time to
tell a story or to build upon knowledge you've earlier presented to the reader.
This blog should provide plenty of opportunities to tell stories and educate
the reader, building upon their knowledge throughout a single post.
-------------------------------------------------------------------------------
Where will this blog go? Will people read it? Will I have the commitment to
keep it alive, or will it die a slow, lonely death like so many others before
it? Only time will tell!
[0]: https://github.com/lihaoyi/mysite
[Web Archive]: https://web.archive.org/web/20111203164223/http://www.techcreation.sg/
[1]: http://lihaoyi.github.io/Ammonite/
[2]: http://lihaoyi.github.io/scalatags/
[3]: http://lihaoyi.github.io/fastparse/
[Hands-on Scala.js]: http://lihaoyi.github.io/hands-on-scala-js/
[4]: http://lihaoyi.github.io/Scalatex/
[5]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Oops, something went wrong.

0 comments on commit bd4a7eb

Please sign in to comment.