Intro I've been building Backdrop , a desktop app, in Clojure for the last few months. I never thought I would build a desktop app, since I mainly did backend work. But I did, with the help of Clojure! If you're curious what Backdrop is, here is a quick demo video I made the other day.
Have you ever been there? You've just finished a brilliant piece of code, designed a slick UI, or found the perfect example for a tutorial. You take a screenshot to share your work, but when you look at it… it's just… plain. You could share it as is, but it lacks professionalism. Or, you could spend the next 15 minutes wrestling with a heavy-duty design tool, carefully placing it on a background,…
In case you don’t know it, we can use raw strings, like embedding JS code, in hiccup. I just found out we can use raw to prevent strings from getting escaped. I used to have to define a dedicated app.js for that, which would need an extra HTTP request. Hiccup raw in action Looking back, I should’ve found this at the very beginning, as it’s just mentioned on its GitHub homepage , but somehow I…
Sometimes, we may want a few examples of a Clojure API for inspiration while developing, especially when reading other people's or open source code. With Cider in Emacs , we can run M-x cider-doc to see the docstring for the symbol. We can even use M-x cider-clojuredocs for some examples if the symbol is from the language core. Running M-x cider-clojuredocs I happened to find this command…
So you're more than excited to find out how Clojure's thread macros can make code clean and concise, you just love it! But at some point you'll be trapped a bit as the threaded argument's positions are inconsistent using -> or ->> . Luckily, you can use as-> to place the argument wherever you like: Lambda vs. thread-as
It's amazingly easy to group things in Clojure. The language core provides a group-by function directly. Together with many built-in functions or key functions, it creates a ton of possibilities, and we can also bake our grouping function if none is satisfying. group-by Example from Clojure Koans .
When you’re building a side project, chances are you need to collect user events so that you can understand user behaviors better and eventually improve your product. So recently I made a small ClojureScript library for that, it supports web apps or chrome extensions. Check it out at https://github.com/kenhuang-io/google-analytics, it also contains a demo project using shadow-cljs as the build…
In Clojure, we can use comment (aka Rich Comment Blocks) for tests or experiments in development. However, since a comment evaluates to nil, you may run into surprising results or even errors if you misuse it. Comment vs. Discard In these scenarios, you may tend to use ; to comment them out, but a better choice is to use the discard reader symbol #_ .
Intro A few years ago, I made a simple web app in Flask to deal with some text processing problems from my daily work. It has two main features: Feature #1: generating compile_commands.json for GNU Makefile projects written in C/C++ using the output of the make command. Because, unlike CMake, the make command can't generate it. Feature #2: extract text using Python regex. It's handy when I feel…
I was wondering how to construct a multi-key map conditionally while I was coding in Clojure. Ideally, I would like to build it "in one pass" like {:bar 2 (when true :baz 3)} , but from what I had collected from Blue Sky, it seemed that's impossible, or it's just not an idiomatic way to program like that in Clojure. (Or, is this just a side effect of writing too much imperative code?)
Here is the outline for my cider tutorial on YouTube, covering basic things you need to know to get started with cider, and starting exploring the fun of clojure programming with the REPL-driven programming approach. Jack In to a REPL C-c M-j (cider-jack-in-clj) start a nREPL and jack in. It works in a project or with a sole .clj file . M-x cider-connect-clj run the command and then fill in…
Recently I've used babashka/fs a little bit, here are some quick notes for it: Path vs File. Use Path whenever possible, according to this SO answer to "Java: Path vs File" . This is actually Java related. It's ok to use a path as a key for a clojure map. At my first try, I somehow came to the conclusion that it's not ok, while I was refactoring the live reload for clay. And later I found that…
While refactoring the live reload feature of Clay , I realized I'd better break long functions into smaller and functional ones (as many as I can), which is also a common practice in the clojure community. Small pure functions not only are easy to verify on the development process (using a REPL), but also are easy to test. And unit tests are easy to write in clojure, just use deftest from…
While developing the live reload feature for Clay , a minimalistic Clojure tool for data visualization and literate programming, I found that it constantly takes ~1 minute for beholder to watch a directory. After some code inspect, I was still having no idea why it happened. So I decided to use a profiler to find out what's going one under the hood, and Google immediately took me to…
Just re-organize the TailwindCSS Docs index in a flatter and more searchable way. Getting Started Installation | Editor Setup | Using with Preprocessors | Optimizing for Production | Browser Support | Upgrade Guide Core Concepts Utility-First Fundamentals | Hover, Focus, and Other States | Responsive Design | Dark Mode | Reusing Styles | Adding Custom Styles | Functions & Directives Customization…
While debugging a live reload issue of Clay a few days ago, I learned from Markus Agwin that we can actually add dependencies on the fly from the command line, just like this: clj -Sdeps "{:deps {org.scicloj/clay {:mvn/version \"2-beta21\"}}}" . Even better, we can add dependency from a Git repo and specify the commit hash: clj -Sdeps "{:deps {io.github.scicloj/clay {:git/url…
While impletenting the live reload feature for Clay a few weeks ago, unfortunately, I was bitten by the laziness of map . It seems quite obvious we all know that its result is a lazy sequence, but I just can't help fall into the "trap". At that time I was adding a vector to keep track of all beholder instances watching file changes in user specified directories. So then when it's time to stop all…
While adding the live reload feature to Clay, I encountered a problem of applying anonymous functions to thread macros. For example, below is a simple snippet trying to wrap the input directory as a vector with the help of thread macro, but surprisingly, it errors out. After some searching, I figured out that I need to put the anonymous function into another pair of parentheses: ( -> "/tmp/" # (…
While troubleshooting and fixing a live reload bug in Clay today, which required to start a minimal Clojure environment, I figured out how to start a nREPL from the command line. Instead of using M-x cider-jack-in-clj directly from Emacs, actually we can manully bring up an nREPL with this: clj -Sdeps "{:deps {org.scicloj/clay {:mvn/version \"2-beta21\"} cider/cider-nrepl {:mvn/version…
It has been some time since I came along the idea of the w/join-lines command to join lines. After that, sometimes I found that it would be even better to join every a few lines. Let's see the example below, suppose we've copies some data from somewhere, and now we want to yank it into an Emacs buffer and slightly modify it to be like an matrix. That is, make it from:
If you've been using Emacs for a while, I bet you must have encountered the annoying problem that your Emacs window layout gets messed up after some operations, such as looking for a help (e.g. C-h k ), or checking things in the magit status buffer. Photo by R Mo on Unsplash I'm happy if I can just get back to the previous layout that I had before, and guess what? Emacs happens to have that…
Long story short, I came up with an idea to emphasize content while I was reviewing an org-mode document a few months ago after I was tired of typing tedious * around the content repeatedly. And I've been longing for this feature since then, because I was unfamiliar with mouse-based key bindings, and neither did I have the time to investigate it. Today I finally nailed it down while I was…
So sometimes I need to join a few lines by a separator while I'm coding, for example, turn the below lines, foo bar baz into foo + bar + baz . (This is a silly example, I will update if I come up with a better one :-P ) When I was in a rush in the past, I usually baked a keyboard macro temporarily and then applied it to achieve this goal, thought reliable, it's a little bit cumbersome to record…
When I started to use Hugo to write this blog last year, I noticed that there is an easy-hugo package of Emacs many people use. So I installed it at that time, but I didn't use many of its features since then. In fact, the only command I used was easy-hugo-current-time . I used it to update the Hugo timestamps manually as in the format of 2022-10-15T09:45:35+08:00 . My most desirable feature is to…
Auto-complete accounts from another accounts.bean file If you're using Beancount with Emacs, you may be using beancount-mode . It can auto-complete the accounts defined in the current buffer when we are typing in new transactions so that we can do it more efficiently. But it can only auto-complete the accounts from the current buffer, which makes it less useful when we have a stand-alone file or a…
In this blog post, I'd like to share a bookmarklet for copying a web page's URL as an org-mode link on Firefox, Chrome, or whatever web browsers support bookmarklets. It's handy when the URL isn't SEO-friendly, which means you can't tell what its content is about at first glance of the URL. So a little description text on the link would help. Here is the bookmarklet: javascript : window . prompt (…
If you are new to Emacs, you may run into some errors, especially after you copied some elisp snippets from the Internet or elsewhere. Don't panic! It happens, it's just part of the learning process. Even an experienced Emacs user could run into there issues from time to time. BTW, I'm improving my English by watching YouTube videos every single day; If you're also learning English, or any…
Duplicate the current in Emacs Duplicating the current line is frequent editing for me when I am coding. Initially, I copied a snippet as a command in Emacs from the Internet: ( defun w/duplicate-line () "Duplicate the current line." ( interactive ) ( move-beginning-of-line 1 ) ( kill-line ) ( yank ) ( open-line 1 ) ( next-line 1 ) ( yank )) Most of the time, I was happy with it, but it has mainly…
Adjust the screen brightness in Emacs If you're using i3wm on Linux, how do you adjust the laptop's screen brightness? Most of the time, I use the laptop with an external monitor. But when I am out, I have no monitors. And it seems no easy way to adjust the brightness in i3wm. Being too bright or dim is terrible for the eyes.
Selecting Emacs windows using ace-window Recently I've been building a simple Emacs config for myself, along the way I re-discovered some fantastic packages, for example, I found that ace-window is a simple yet powerful package to enhance the default other-window command to select other windows quickly when it has more than two windows in a frame. I replaced the key binding of other-window to…
Scrapy Architecture Scrapy provides a few item exporters by default to export items in commonly used file formats like CSV/JSON/XML. I usually use CSV to export items, it is pretty convenient, and it comes in two ways: appending mode, for example, scrapy crawl foo -o test.csv overwriting mode with -O option, like scrapy crawl foo -O test.csv But in the appending mode, it's a bit annoying that it…
Hey, welcome to my blog site! This is Ken, who is passionate about technology and a lifelong learner. Here I share my experiments and thinking on technology, especially Clojure/ClojureScript and Emacs stuff. Bookmark this site or follow me on social media, the links are at the bottom. Besides, I also make and post videos to my YouTube channel from time to time, please subscribe if you're…
LSP , or Language Server Protocol, makes programming easier by introducing features like more precise auto-completion and definition lookup. It may have scratched your itches, and you may wonder what the experience is like in Emacs. BTW, I'm improving my English by watching YouTube videos every single day; If you're also learning English, or any languages, LanguagePuppy can definitely help you.…
Sometimes I want to change the status ( i3status ) of i3wm temporarily, but it seems that i3wm doesn't support it directly, although reloading the config for i3wm itself is a piece of cake ( bindsym $mod+Shift+c reload in the config, or i3-msg -t command reload in the command line). But this issue scratched my itch, and I swear that I must solve it today. BTW, I'm improving my English by watching…
So I'm trying to improve my English speaking skill by shadowing while watching TV episodes. The workflow before was to loop over video clips using mpv: hit l to mark the start of the loop play the video and wait for it to be at the end of the loop hit l again to mark the end Then mpv will loop over the clip, it basically works, but it's a bit hard and tedious to set the start and end precisely.
Note: watch my live coding session of this article: Intro If you've heard some rumors of Emacs that it has a very steep learning curve (or that Emacs makes a computer slow), you may be too scared to look at it. It indeed has some learning curve (learning anything does have one), but it isn't very steep. I learned this after getting my hands dirty with Emacs a few years ago.
Beautiful Soup Detailed docs: the Beautiful Soup 4 Docs . Assume t is an object of Tag . Core concepts (classes) Tag , a Tag object corresponds to an XML or HTML tag. BeautifulSoup , the BeautifulSoup object represents the parsed document as a whole. You can treat it like a special Tag. It needs a parser to parse the document, a built-in parser is "html.parser" , e.g. soup = BeautifulSoup("<html>a…
As a practice, I managed to illustrate Clojure files using illustrate.clj , but my original idea was to annotate org-mode files of blogs. It's not uncommon that a blog post has some code snippets. But it missed the feature until last night, as I wasn't sure how to implement it appropriately before and didn't have enough time. For example, I may have an org-mode like this: sum of two numbers:…
To get my hands dirty with Clojure, I am trying to find or implement Clojure's string functions in the sense of Python. Python has powerful string APIs, and I also want to see how powerful Clojure could be in this field. That would be interesting. As shown in the cheatsheet , Clojure has implemented most of them, and there are some that I have to implement myself, like title-case . Along the way,…
These days I like to write scripts for some tasks in Python instead of shell. One important reason I think that's because Python is powerful at string manipulation. Recently I'm learning Clojure, and I'm trying to find similar ways in Clojure, one of them is s.title() for getting a title-cased version of a string. For example, >>> ' Hello world'.title() ' Hello World' How to do that in Clojure? To…
Python string APIs are powerful and concise, that is an important reason I use it to do a lot of scripting these days, join , split , strip , to name a few. Since I am learning Clojure recently, I am wondering, how is string manipulation like in Clojure and how to implement equivalent ones? I think it's an excellent opportunity to get familiar with Clojure. Before diving into the implementation,…
I barely use notifications, but recently I think it's a valuable way to remind me things like helping me nurture habits, or to notify me of emergencies like a critically low laptop battery. So I try to integrate notifications to org-mode and Emacs today. The org-notify package from org-contrib (install it by (package-install 'org-contrib) ) could do this job easily before, so I first test it in…
I'm used to learning by practicing, so when I learned Clojure, I always kept an eye on chances to write code in it. Scripting is an excellent field to practice, but the experience is not so good. On the one hand, it's too hacky to wrap Clojure code in a shell script with the shell bang. On the other hand, the startup time of JVM is too long to hurt the user experience.
As a programmer, I know that grep , sed and awk are powerful for processing text, but they sometimes aren't that straight-forward for specific tasks, as I need to think about how to filter the lines and the columns out. So I wonder if there is a handy way to do these tasks? After using it for a while, I think using regex directly can help, so I launched a re.findall service building on top of…
Doxygen is a nice tool for generating documentations for well-annotated C/C++ projects, the one feature that I like most is generating call graphs and class diagrams, so that I can learn a project quickly by browsing the diagrams from a higher point of view. I take the following steps to generate call graphs for a project on terminals on Linux: cd /path/to/a/project/ , and generate a template…
Traditionally we are used to storing data in an RDBMS like MySQL, and avoid using in-memory solutions such as Redis, to have a confidence of no data loss. Sometimes I find that we are so stubborn with MySQL that ending up with a complicated design, and I've even seen a solution that stores data in MySQL and then using Redis as a cache for it to improve read performance in the meanwhile. And to…
There are many packages of Emacs that leverage subprocesses to do their jobs, Magit , eglot , elpy , to name a few. And there are times that a subprocess doesn't work as expected, for example, Magit is slow, and you're sure that it's ok when running git commands on shell. So how to spot these problems effectively and quickly? The problem is that we don't know what's going on exactly, so here I…
If you are a C/C++ programmer like me, you may experience jumping around different machines to write and compile your code, or you may write code on different machines for different projects, and scp/rsync/ftp the projects around. It works, but it's a bit tedious and takes too much burden on our brains. For example, say I have two projects that should be compiled on their compiling machines…
Binary search algorithm is a search algorithm that finds the position of a target value within a sorted array. It cuts off the target array in half in a pass, so that it has a worst-case performance of O(log n) . Visualization of the binary search algorithm where 7 is the target value(@wikipedia) We all know that it's an efficient searching algorithm, but the strategy behind it also applies for…
I used to manage SSH connection with such GUI apps as MobaXterm , when I wrote code on Windows at work. As I changed my workflow to use a tiling window manager in a VirtualBox guest OS, I continued to improve my way of managing SSH connections, which I want to share here if you don't know yet. At first, I baked a helper Python script, which I named as qssh for "quick ssh", to help me assemble ssh…