20:24 rson: if there is anything i could ever suggest that you'd listen to, let
it be this. do it.Wise words from someone who’s been there before. That’s rson telling me that I should move my site to some sort of framework. Make things cleaner, easier to maintain, and get away from that goddamn php I seem to be so fond of.
I had been thinking about doing this myself for quite some time. As silly as it sounds, I was unhappy with my urls. The whole site (from a purely url-appearance standpoint) was inconsistent. I dreamed for /feed/ and /posts/my_post/.
I could also feel my spider web of php and html spiralling away from me. I was spending too much time monitoring comments, tweaking the syntax highlighting, and figuring out the best way to format bread crumbs based on not only filepath but also custom translations from content.php to all posts and similar.
Then I found Yesod, a web framework based on haskell. As anyone who’s ever been to this site knows, I love haskell. It’s just a cool language. So if I were going to move to some sort of framework, this would be it.
So, using the Yesod Docs, the haddock documentation, and even the actual source for the Yesod Docs, I was able to hobble my site over to the framework. It wasn’t easy, but there’s a lot of benefit there.
My breadcrumbs went from 100 lines of php to about 14 lines of haskell. And those 14 lines are simply defining what Routes are children of what other Routes.
My posts have tags now. This extra bit of post-metadata was even added later without disrupting any existing code.
My Rss feed is dynamically created whenever it’s loaded.
And probably most important of all, urls used throughout the site are type safe, compile-time-guaranteed to be valid.
What that means is that I don’t type the url directly, I insert a haskell function that corresponds to those pages’ Routes. And no, they aren’t built from regular expressions; each Route is generated as a distinct type as defined by me.
Routes can also have arguments. Right now you’re viewing the output of the PostR Route using site_migration as its argument. But the best part of all that is that the compiler validates every link in my site each time it’s compiled to ensure it’s in scope and type checks!
As part of the transition, I’m also giving up some control over code snippets and comments. I enjoyed the DIY approach but it was getting cumbersome (and less and less KISS as things went on).
Instead, I’m stealing two more ideas from the Yesod Docs site. The new site uses git’s gist feature for code snippets and disqus for comments. I know, I originally said I, “didn’t want to farm comments out to 3rd party javascript,” but disqus is really nice and I’m getting sick of all the overhead that comes with my homebrew php setup.
I’m really sorry to anyone who’s left comments so far on the site. I appreciate them greatly. I still have them and I’ll continue to look into ways to port them over to disqus, but so far, it’s not looking too promising.
I’ve changed my approach to posts and am now using pandoc to write them. This means that I don’t need gist anymore thanks to pandoc’s great syntax highlighting features. I’m also working on my own Yesod module for Comments to get things back the way it was on the old site. That’s a bit of a work in progress at the moment and will be its own post when it’s done… I’ll be keeping disqus around for a while.
Another change I’m making is from Apache over to Lighttpd (pronounced: lighty). To be honest, I just couldn’t get (Fast)CGI working with apache and I had it running with lighttpd in minutes. Hopefully it’ll be faster and easier to maintain too, we’ll see…
So anyway, enjoy the new site; let me know if anything is broken or missing – I’m still in the process of migrating old posts, so give me some time before reporting that.
The site’s source is also in my git repo if anyone’s interested.
published on Oct 10, 2010, tagged with haskell, websiteThis page is to serve as both an apology and an announcement. I’ve recently modularized my xmonad.hs. I’m sorry.
This is no longer true. I’ve since gone through a bit of a config cleanse, deciding it makes my life easier to live closer to defaults and not carry around a lot of extra configuration or features (that I don’t actively use).
As part of this cleanse, I’ve stripped my config back down to a very leanxmonad.hs that can easily live within the confines of a single file.I know of at least one person who stops by my site on a regular basis to update his xmonad.hs to match the latest version of mine. I’ve also seen, on a few occasions, someone mention that they use brisbin33’s xmonad config when discussing an issue on the forums or in IRC. True, for all I know, there could be only three people using some form of my config – but to them, I’m sorry.
Anyone who blindly updates to my most recent xmonad.hs may get hit with the following error:
xmonad.hs:21:7:
Could not find module `ScratchPadKeys':
Use -v to see a list of the files searched for.
Failed, modules loaded: none.That’s because I’ve offloaded some of the more module-ish chunks of my config into, well, modules.
I noticed, when browsing the XMonad source (I know, shut-up), that the default recompile command includes the option -ilib this tells ghc to include source files in ./lib. It was a light-bulb moment.
I had gathered some pretty sophisticated code in my little xmonad.hs: custom data types and instances, reusable utilities, etc. Why not put them in their own files and import them into a nice clean config as I would with any normal contrib module?
So, if you’re following my xmonad.hs, please continue to do so. Just be advised you’ll need a few files in lib if you want to use the functionality they offer.
I’ve been looking for a good Haskell project for a while now. The language is just awesome, and I’ve been getting more and more comfortable with it lately thanks to reading Real World Haskell. I even got the opportunity to write some haskell for a project at work (I’m a consultant on a Microsoft product, crazy).
I wanted something challenging but doable; something to keep me interested but still stretch my abilities. I had made some smaller utilities to manage the pages on my site, so I was getting familiar with parsing XML using some haskell libraries as well as starting to wrap my head around the IO Monad a bit more. Well, I just completed (what I think is) a slick little RSS reader using just haskell and dzen.
For those that don’t know, RSS feeds are basically just site headlines; a very simple XML page that lists items, each item containing a title, description, and link.
So my reader would read in a listing of feed urls, put together all of the RSS items from each url, and then display them using dzen.
I put it in the upper right of my left monitor, configured to look like part of my existing dzen status bars.
The title text remains static and is clickable (opens the url of the feed item), and the description text is a ticker text that rolls by right-to-left one character at a time.
First, you would have to download RssReader.hs and Dzen.hs from my old xmonad library and place them in a directory along side a file called rssreader.hs. This file would serve the same purpose xmonad.hs does for XMonad: it would be both a configuration file and the main application itself, gluing together imported functions into a runnable main.
Here’s an example:
import Dzen
import RssReader
--
-- this is it, the whole application in one line!
--
main :: IO ()
main = spawnDzen dzenConf >>= spawnReader readerConf
--
-- and the configuration part...
--
-- set a width and some text formatting
readerConf :: ReaderConf
readerConf = defaultReaderConf
{ titleFormat = dzenFG "#909090"
, descrFormat = shorten 200
, tickerWidth = 150
}
where
-- some helpers
dzenFG c s = concat ["^fg(", c, ")", s, "^fg()"]
shorten n s = if length s > n then (take n s) ++ "..." else s
-- start with the default dzen and override some things
dzenConf :: DzenConf
dzenConf = defaultDzen
{ x_position = Just $ Percent 60 -- start 60% across screen 0
, width = Just $ Percent 40 -- and span the other 40%
, font = Just "Verdana-8" -- if you have an xft-capable dzen
, fg_color = Just "#606060"
, bg_color = Just "#303030"
}Once that’s all set, you can run ghc --make -o rssreader rssreader.hs inside this directory to create an executable which you can run standalone.
The following packages would be required either from Hackage or your distribution’s package manager:
| Hackage | Arch linux |
|---|---|
| http | extra/haskell-http |
| tagsoup | aur/haskell-tagsoup |
Some unprintable characters seem to still come through. I try to clean the strings as much as possible, but I still see boxes in dzen from time to time.
The rssreader and the spawned dzen are not tied together process-wise. This means that you can kill rssreader and a frozen dzen remains, or you can quit the dzen and rssreader will be left as a zombie.
published on Aug 15, 2010, tagged with dzen, haskell, xmonadIf you’ve read my recent post on using a scratchpad in XMonad, and if you’ve actually implemented this in your own setup, you probably know how useful it is. For those that don’t know what I’m talking about, you basically setup a simple keybinding that calls up a terminal (usually floated, but managed by its own specific manageHook) to be used briefly before being banished away by the same keybinding.
Recently, I found that you can apply this functionality to any application you’d like.
I have my music playing through MPD all the time. Occasionally, I’ll like to play some other media, a youtube video or what have you. When I do this, I call up ossxmix, adjust down MPD, and adjust up my browser (per application volumes are awesome by the way).
I realized that this was a perfect scratchpad scenario. I was calling up this application for just a second, using it, then sending it away. This simple activity was requiring that I M-p, type ossxmix, hit enter, layout-shuffle, adjust volumes, then M-S-c every single time. What was I thinking?
My last writeup used the contrib module XMonad.Util.Scratchpad which, though it has a shorter name, simply provided wrapper functions for the things I’m now using from XMonad.Util.NamedScratchpad.
In the parent extension, things are much more transparent and free. For me, this lead to a much cleaner config file too. I wish I had been using things this way from the start.
So of course, we’ll need to add import XMonad.Util.NamedScratchpad to the top of our config file.
myManageHook and myKeys to be defined as separate functions. I also won’t be going into hiding the NSPThe Named Scratchpad extension exposes a new data type that can be used to represent a scratchpad. The following four things must be specified to fully describe a scratchpad:
(Query Bool --> ManageHook) and one representation of this might be (className =? "Firefox" --> doFloat) that should give you an idea of the sorts of functions that you should use to fill those last two slots for your scratchpads.The haddocks for this module talk about everything that’s available, but here’s a commented version of my declaration:
myScratchPads = [ NS "mixer" spawnMixer findMixer manageMixer -- one scratchpad
, NS "terminal" spawnTerm findTerm manageTerm -- and a second
]
where
spawnMixer = "ossxmix" -- launch my mixer
findMixer = className =? "Ossxmix" -- its window has a ClassName of "Ossxmix"
manageMixer = customFloating $ W.RationalRect l t w h -- and I'd like it fixed using the geometry below:
where
h = 0.6 -- height, 60%
w = 0.6 -- width, 60%
t = (1 - h)/2 -- centered top/bottom
l = (1 - w)/2 -- centered left/right
spawnTerm = myTerminal ++ " -name scratchpad" -- launch my terminal
findTerm = resource =? "scratchpad" -- its window will be named "scratchpad" (see above)
manageTerm = customFloating $ W.RationalRect l t w h -- and I'd like it fixed using the geometry below
where
-- reusing these variables is ok since they're confined to their own
-- where clauses
h = 0.1 -- height, 10%
w = 1 -- width, 100%
t = 1 - h -- bottom edge
l = (1 - w)/2 -- centered left/rightSo you can see I have a list containing two scratchpads. The datatype syntax requires the “NS” plus the four things I’ve listed above.
The beauty of all this is that it’s almost all that’s needed. Each scratchpad has a name which can be bound to a key; even better, the whole scratchpad list will be managed with one simple addition to your manageHook.
I inserted the following keybindings:
myKeys = [ ...
, ...
, ("M4-t" , scratchTerm )
, ("M4-S-m" , scratchMixer)
, ...
]
where
-- this simply means "find the scratchpad in myScratchPads that is
-- named terminal and launch it"
scratchTerm = namedScratchpadAction myScratchPads "terminal"
scratchMixer = namedScratchpadAction myScratchPads "mixer"And tacked the following onto the end of my managehook:
myManageHook = ([ -- whatever it might be...
, ...
, ...
-- this manages the entire list of scratchpads
-- based on the query and hook listed for each
]) <+> namedScratchpadManageHook myScratchPadsThat’s it, a scratch terminal and a scratch mixer; but most importantly, simple and transparent tools for adding any arbitrary application (graphical or in-term) as a scratchpad application.
One final note about testing: As you’re tweaking your queries and hooks, be sure to call up the application, close it, then Mod-Q and test your changes. If you’ve got a scratchpad still open from before your last config change, it will still be using the old ManageHook.
published on Jun 14, 2010, tagged with haskell, xmonadIt’s been a while since I’ve made an XMonad post. Thought a good one might be details regarding the scratchpad extension from -contrib.
This can be confusing to set up, but oh-so useful. If you’ve ever used a quake (or yakuake?) terminal (I have not), you’ll know what I’m talking about. It’s basically a small terminal that sits idle on a non-visible workspace. You can call it up with a quick keybind, use it for whatever, then banish it away again with the same keybind.
You just have to use it for a while to realize how useful it really is.
My goal for this post is to distill out of my xmonad.hs just the scratchpad functionality so that someone with an existing xmonad.hs could easily plug this into their setup with minimal fuss.
I’m going to assume that your existing xmonad.hs defines a function called myManageHook and another called myTerminal. If this is not the case, take a look at the below snippet; I think you’ll be able to figure out how to rework whatever you do have into this format.
main = do
xmonad $ defaultConfig
{ terminal = myTerminal
, manageHook = myManageHook
, ...
, ...
}
myTerminal = "urxvt"
-- you could have some crazy long managehook
-- or simply defaultManageHook
myManageHook = ...You’ll need to import some things to make this functionality available.
Make sure you’ve got -contrib installed and add the following to the top of your xmonad.hs:
import XMonad.Util.ScratchpadPretty easy, huh?
We’re going to add an additional manageHook to manage the scratchPad specifically. XMonad makes it easy to just tack manageHooks onto whatever you have existing by using <+> which is an inFix operator that takes two manageHooks and returns a manageHook. So…
myManageHook = ([ -- whatever it is, probably some list of things...
, ...
, ...
]) <+> manageScratchPad
-- then define your scratchpad management separately:
manageScratchPad :: ManageHook
manageScratchPad = scratchpadManageHook (W.RationalRect l t w h)
where
h = 0.1 -- terminal height, 10%
w = 1 -- terminal width, 100%
t = 1 - h -- distance from top edge, 90%
l = 1 - w -- distance from left edge, 0%What I’ve done is used RationalRect to define a rectagle of where I’d like the scratchpad to ppear. h, w, t, and l are entered as percentage screen size. So in the above, I’ve got a rectangle that spans the monitor’s entire width and is 10% its height. By specifying h and w, t and l are already defined since I want it to be on the bottom edge of the screen.
I’m not really going to get specific with the key binding part. Personally, I use EZConfig. Everyone seems to have their own syntax/style of binding keys in xmonad; usually it’s just the way it was in the first config you copied from, whatever. Just know that someway-somehow you’ll need to bind a key to…
myKeys = [ ( ... , ... )
, ( ... , scratchPad ) -- spawn a scratchpad terminal
]
where
scratchPad = scratchpadSpawnActionTerminal myTerminalMake sense?
At this point, you should have a functioning scratchpad. Remember, any changes to the manageHook require you to exit and reopen the scratchpad terminal to see the effect.
Using this scratchpad module creates a workspace called NSP where the scratchpad resides when it’s not visible. You’ll notice, this workspace will show up in any dzen or xmobar you’ve got going on. But with some changes to our logHook we can filter that out of the workspace list pretty easily.
If you’re not using a custom logHook, you’ve pretty much got two choices at this point: head over to the docs on xmonad.org and find some drop-in filter-out-NSP module and figure out how add it (I know it’s there but I could not for the life of me get it working), or just figure out how to get a custom logHook going.
What I’m about to go into assumes you’ve already got something like the following defined in your xmonad.hs:
myLogHook h = dynamicLogWithPP $ defaultPP
{ ppCurrent = dzenColor color1 color2 . pad
, ppVisible = dzenColor color1 color2 . pad
, ppUrgent = dzenColor color1 color2 . pad . dzenStrip
, ppLayout = dzenColor color1 color2 . pad
, ppHidden = dzenColor color1 color2 . pad
, ppHiddenNoWindows = namedOnly
, ppTitle = shorten 100
, ppSep = " "
, ppWsSep = ""
, ppOutput = hPutStrLn h
}The above requires other contrib modules, changes to main, and special imports to get working. As I’ve said, I’m leaving it as an exercise for the reader to set up his or her own logHook.
Once we’ve got this, filtering out the NSP workspace is pretty straight forward. Here’s the above again, but this time with the NSP workspace filtered out, hopefully you’ll be able to modify things as needed to make this work with your setup.
myLogHook h = dynamicLogWithPP $ defaultPP
{ ppCurrent = dzenColor color1 color2 . pad
, ppVisible = dzenColor color1 color2 . pad
, ppUrgent = dzenColor color1 color2 . pad . dzenStrip
, ppLayout = dzenColor color1 color2
, ppLayout = dzenColor color1 color2 . pad
, ppHidden = dzenColor color1 color2 . pad . noScatchPad -- haskell makes it so easy,
, ppHiddenNoWindows = noScratchPad -- just tack on another function
, ppTitle = shorten 100
, ppSep = " "
, ppWsSep = ""
, ppOutput = hPutStrLn h
}
where
-- then define it down here: if the workspace is NSP then print
-- nothing, else print it as-is
noScratchPad ws = if ws == "NSP" then "" else wsGood luck!
published on Apr 10, 2010, tagged with haskell, xmonad