Sacha Chua: 2020-03-30 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file and emacs-devel.

-1:-- 2020-03-30 Emacs news (Post Sacha Chua)--L0--C0--March 30, 2020 11:57 PM

Raimon Grau: Simplicity is its own reward

This is 3 top level hackers discussing about a patch that Stefan proposed to an already solved problem (I guess originally by Daniel, or someone else, but Daniel is anyway the father of the pdump feature).

Amazingly classy and to the point. All 3. I'm learning so much reading this maillist. And still a lot to learn.

Bravo, and thanks.


Stefan Monnier: >> Any objection?
>
Eli Zaretskii: > What are the advantages?  The original problem is solved, and
> everybody agreed that having a dead buffer in the pdumped area is
> nothing we should bother about.
>

Daniel Colascione: With Stefan's patch, Emacs is simpler. Simplicity is its own reward.

Original thread: https://lists.gnu.org/archive/html/emacs-devel/2020-03/msg00922.html
-1:-- Simplicity is its own reward (Post Raimon Grau (noreply@blogger.com))--L0--C0--March 30, 2020 11:54 PM

Irreal: Implementing a Zettelkasten with Org Mode

In a perfect example of the Baader-Meinhoff Phenomenon, a month ago I had never heard of the the term Zettelkasten and now it seems to be everywhere I look. Previously, I wrote about the idea and noted that it was a perfect application for Org Mode. I’ve been experimenting with the concept a bit and using Org Mode to store and search for the data. It’s just preliminary futzing about. When I get around to using a Zettelkasten for real, I’ll probably use Jethro Kuan’s org-mode package, at least as a starting point.

In the mean time, Dan Pittman has his own Org Mode based Zettelkasten implementation. Like mine, it’s fairly elementary. He has a Bash script that captures the note title and pops him into Emacs to fill in the initial data. Like me, he uses Org agenda to search his notes by either tags or keywords. Other than the Bash script—which you could easily do without—there’s no special code: it’s just standard Org Mode. Pitmann keeps each “card” in a separate file with the the note’s date/time in the file name. That way, he can use the file name as the card ID. That’s a better strategy than what I do (using an org-id-get-create generated :ID: in a property drawer) because it’s much easier to link ideas.

I really like the Zettelkasten idea and have been collecting posts about ways to implement one. If you’d like to try it out too, Pittman’s post shows you how to get started in an easy way with a small investment in time.

-1:-- Implementing a Zettelkasten with Org Mode (Post jcs)--L0--C0--March 30, 2020 05:36 PM

Manuel Uberti: Lockdown Beam: git-identity

Lockdown Beam

The situation doesn’t seem to get any better, does it? We are still confined in our homes, still wary about our movements and contacts. Since not even running outside is allowed, I am getting lazy, to the point that I’ve never thought I’d miss the gym so much. It’s time to resort to in-house crunches and push-ups, I suppose.

Meanwhile, let’s not forget about Emacs. Last time I hinted at a Git-related package, but at this point wouldn’t writing about Magit again be out of fashion1?

When it comes to Git repository, the only thing I am not using Magit for is checking and setting my identity. I’m not sure this is a common problem, but I have to handle two Git identities on the same machine, one for personal repositories and one for work projects.

Without Akira Komamura’s git-identity this situation was leading me to confusion, because more often than not I found myself committing and pushing work stuff with my personal identity. One could manually edit .git/config every time or use Git’s own commands to deal with this, but to me these are both cumbersome alternatives compared to the simplicity of Konamura’s package.

The only configuration needed for me after the installation was binding I to git-identity-info in magit-status-mode-map. Now once I cloned a new repository I can easily check whether the identity is correct or not.

Furthermore, git-identity can help even if for some random reason I didn’t rely on git-identity-info. With git-identity-magit-mode enabled, git-identity makes sure that on magit-commit a global or a local identity is actually set.

Next time we are going to play hide and seek with the mode-line.

Stay safe.

Notes

  1. I am kidding, Magit is always stylish. 

-1:-- Lockdown Beam: git-identity (Post)--L0--C0--March 30, 2020 12:00 AM

Irreal: A Repository for Writing a Thesis With Org Mode

Back in 2018, I wrote about a post by Daniel Gomez that describes how to use Org-mode for writing a thesis. At the time, Gomez was in the process of writing his own thesis and his post details his work flow and files structure. It’s an excellent how-to for anyone wanting to use Org for writing their own dissertation and well worth your time if you’re looking for a good thesis writing tool.

An the end of the post, Gomez says that he was considering building a thesis template repository to go with his post but that it would have to wait until after he’s finished his thesis. Now that he’s finished his PhD, Gomez has in fact published his templates as a GitHub repository.

It’s a valuable resource for anyone embarking on the writing of a dissertation, especially if, like Gomez, the thesis consists of three or four research chapters that are published separately as journal articles. With his setup, the exact same source is used for publishing the chapters as journal articles and as part of the thesis. Check out his post or the repository to see how he did this.

As I said in my original post, Org-mode really is a general purpose writing tool that can be used for memos, blog posts, reports, books, and even things like theses with their strict formatting rules. I do all my writing—except emails and texts—with Org and can’t imagine a better tool.

-1:-- A Repository for Writing a Thesis With Org Mode (Post jcs)--L0--C0--March 29, 2020 04:17 PM

Alvaro Ramirez: String inflection Emacs package

29 March 2020 String inflection Emacs package

string-inflection (by Akira Ikeda) is a nifty package to cycle through string case styles: camel, snake, kebab… The package includes a handful of cycling functions for different languages (Ruby, Python and Java), but it's easy to mix and match to roll your own. For now, I'm binding C-M-j to string-inflection-cycle, which is an alias to string-inflection-ruby-style-cycle.

( use-package  string-inflection
   :ensure t
   :bind ( :map prog-mode-map
              ( "C-M-j" . string-inflection-cycle)))

string_inflection.gif

comments on twitter

-1:-- String inflection Emacs package (Post Álvaro Ramírez)--L0--C0--March 29, 2020 12:00 AM

Irreal: Zamansky 70: Org Protocol

If you’ve been following Irreal for a while, you know that I periodically try to get Org Protocol integrated into my workflow and end up writing a post on how I failed yet again. The last such post was just a month ago. Now Mike Zamansky has shamed me with the latest video in his Using Emacs Series. The latest video describes how he got Org Protocol to capture links to Web pages and emails.

Org Protocol helps with the link between your browser or email client and Org Mode. Most of the actual work flow is implemented with Org capture templates in the usual way. Zamansky found a browser extension, org-capture-extension, that makes setting up the browser/org protocol link easy. It provides a button that will send the necessary information to Org Protocol so that the Org capture mechanism can deal with it.

Setting up Org Protocol can be very fussy so it’s nice to have Zamansky’s step-by-step video as a guide. The Org-Capture extension is not available for Safari so I can’t use his solution directly. Fortunately, I handle the browser/Org interface with the bit of Apple Script that I wrote about back in 2014.

If you’re running under Linux and want to capture links from your browser or, say, Gmail, be sure to take a look at this video to see how to set things up. If you’re running under Windows, Sacha has some wisdom on setting things up (it’s from 2015 but can probably still be made to work).

The video is 11 minutes 40 seconds long so it should be easy to find time for it.

-1:-- Zamansky 70: Org Protocol (Post jcs)--L0--C0--March 28, 2020 05:11 PM

Meta Redux: nREPL 0.7

It has been a little over a year since the release of nREPL 0.6. Moreover, it has been over nine months since I wrote that nREPL 0.7 was getting a native EDN transport. So, where the heck is going with nREPL? Is it stuck again so soon after its revival? I can assure it that’s definitely not the case and despite the lack of new releases nREPL has been making some good progress. I’m also extremely happy to announce that nREPL 0.7 is finally here! COVID-19 did a lot of bad things, but it also cleared my busy schedule rather dramatically, so lately I’ve been trying to catch up on all of my important projects. There has to be something good coming out of this horrible madness, right?

So, what’s new and noteworthy? I already wrote about the EDN transport, so I guess I can skip that part. There are two other big additions:

  • Clients can now inject code remotely in a running server on demand. This probably doesn’t make any sense to most of you, but it’s insanely cool and powerful. We’re calling this feature “side-loading”, following the example of unrepl, which pioneered it.
  • nREPL responses are now covered with clojure.spec.

Let me expand a bit on the sideloader, as I think it’s the most interesting addition in 0.7. In simple terms it will allow a client to provide missing code on demand. Here’s a short example:

;; -> init sideloading
{:op      "sideloader-start"
 :id      "1"
 :session "x"}

;; -> try to require a missing namespace
{:op      "eval"
 :id      "2"
 :session "x"
 :code    (quote (require '[foo.bar :as bar])
                 (bar/qaz))}
;; <- lookup for foo.bar
{:id      "1"
 :session "x"
 :status  :sideloader-lookup
 :type    "resource"
 :name    "foo/bar.clj"}
;; -> providing resource
{:id      "3"
 :session "x"
 :op      "sideloader-provide"
 :type    "resource"
 :name    "foo/bar.clj"
 :content "<base64 package>"}
;; <- ack of provided resource
{:id      "3"
 :session "x"
 :status  :done}
;; <- result of eval
{:id      "2"
 :session "x"
 :value   "Qaz"
 :status  :done}

What’s happening here? The client tries to require foo.bar, but the library is not available locally. That’s why nREPL asks the client to provide the missing library. The client responds with a Base64 encoded payload that’s the missing resource in question, this gets loaded in nREPL and the evaluation continues. The potential to utilize this is huge, because now clients can enhance running servers (e.g. with code completion) without the need to include additional dependencies when booting nREPL. I wanted to implement this in CIDER first, but I was crazy busy the past few months, so there’s a good chance that some other client author is going to be beat me to it. That is totally fine! Please, beat me to this and build something cool with the sideloader!1 There’s a bit of documentation about it here.

There have been other small tweaks and improvements in the latest release as well. For all the gory details you should check out the release notes. One thing that’s not covered there is that nREPL’s documentation got some love, so it’s better than ever. Still, there’s a lot of room for improvement, especially when it comes to the protocol’s specification, so it’s easier for folks to build alternative implementations and new clients.

I already have some (modest) plans for nREPL 0.8 - the main focus there is going to be the addition of built-in completion and info ops, so that clients would have access to even more useful functionality out-of-the-box. You can read more about this here. There are also plenty of other open tickets, so if you’d like to help out you’ll have your pick of some mighty fine options (many of which are (somewhat) beginner friendly).

I’m hoping that the improvements in nREPL 0.7 and the planned improvements for nREPL 0.8 are going to contribute to the creation of more and better clients for nREPL in the years to come.

I’ll also use this post to share some cool general updates about nREPL:

  • Chris Badahdah is working on a ClojureScript port of nREPL.
  • Michiel Borkent is working on a Babashka port of nREPL. His basic nREPL implementation is also a good example of how easy it is to implement the nREPL protocol.
  • Maurício Szabo is considering adding nREPL support to Chlorine.
  • Oliver Caldwell is considering adding nREPL support to Conjure.
  • Pratik Karki announced at IN/Clojure that LightTable will be dropping their custom nREPL and adopting the modern nREPL + everything in CIDER’s Orchard.

Such developments make me extremely happy and reaffirm my belief that nREPL will continue to play a central part in the future of Clojure’s development tooling (and hopefully beyond Clojure). By the way, you might also want to check out my recent IN/Clojure presentation on that topic.

Time to wrap it up now. Special thanks go to Shen Tian for his work on the EDN transport and the sideloader. Christophe Grand also deserves a lot of credit of coming up with the sideloader in the first place and building a prototype for nREPL. Thanks a lot, guys! You rock!

That’s all I have for you today! Keep hacking!

P.S. If you haven’t spent all of your money on toilet paper, you might consider supporting nREPL via GitHub Sponsors and OpenCollective.

  1. A couple of days after I wrote this post vim-iced became the first editor that added support for the side-loading functionality! You can find more details here

-1:-- nREPL 0.7 (Post Bozhidar Batsov)--L0--C0--March 28, 2020 02:19 PM

Mike Zamansky: Using Emacs 70 Org Protocol

I spent part of today cleaning up my Emacs workflow. Specifically, how I capture emails and links into org-mode I already wrote about how I used org-capture (here and here). It's pretty clean and easy but there was one thing that always nagged at me. When I capture from mu4e within Emacs by hitting C-c m it's set up to automatically populate the capture template with a link to the email labelled with the email's subject.
-1:-- Using Emacs 70 Org Protocol (Post)--L0--C0--March 27, 2020 07:15 PM

Meta Redux: Adjusting RuboCop’s Defaults

Since the beginning of time certain people have been unhappy with RuboCop’s defaults. I guess that’s one of the universal truth in life - no matter how hard you try you can never please everyone. Especially the users of a lint tool.

Still, with RuboCop 1.0 just around the corner, I’ve decided to run a short survey so we can see if some defaults really don’t make sense. The survey covers only the topics that I remember had generated the most heated discussions throughout the years (e.g. single-quoted vs double-quoted strings), but I’ve also added an open-ended question that allows you to share whatever else frustrations you might have with the default configuration.

We’ve avoided changing the defaults in the past couple of years (part of our general effort to reduce the friction of RuboCop’s upgrades) and we’ll really limit those after RuboCop 1.0. It seems that’s our final opportunity to make some sweeping changes and make people royally pissed.

I’ll leave the survey open for a couple of weeks1 and might potentially extend the list of questions if I notice some trends in the answers to the open-ended question. Once the survey is closed I’ll publish the results online, as I assume they will be of interest to many people.

Note that changes to RuboCop’s defaults will be made only if the survey results indicate a significant difference between a current default and its alternatives (e.g. >10%). If we’re looking at numbers like 48% to 52% it’s not worth doing any updates as any of the values would be just as good of a default. No point in breaking anyone’s setup without a solid reason to do so.

Please, share the survey around - the more responses we get, the better our actions are going to be. That’s everyone’s simplest way to influence the future direction of RuboCop!

That’s all I have for you today! Keep hacking!

  1. Currently I plan to close the survey on the 12th of April, but this date might change depending on daily number of the respondees by then. 

-1:-- Adjusting RuboCop’s Defaults (Post Bozhidar Batsov)--L0--C0--March 27, 2020 09:24 AM

Meta Redux: The Books That Every Programmer Should Read

I read relentlessly.

– Rich Hickey, creator of Clojure

Several years ago1 I did a talk at HackConf titled “The Books That Every Programmer Should Read”2. Back then the conference was geared mostly towards young aspiring software engineers, so I wanted to present on a topic that they’d find useful. Early on in my career access to high-quality educational resources on programming was very limited - the Internet was quite barren by modern standards and there were very few online resources, foreign books were very hard to procure in Bulgaria, and there weren’t that many experience people to learn from. I’d tackle every book I could lay my hands onto, and as a result I wasted a lot of valuable time that I could have utilized better. On the bright side - after a while I could immediately tell if some book was going to help me level up my skills or not. I wanted to share my experience and help others prevent some of my mistakes, even if we live in a very different world today, and books might be starting to lose some of their relevance.

While, I didn’t feel that my talk was particularly special in any way, it became one of my greatest hits and I’ve been often asked to discuss certain aspects of it. Another thing that I’ve been asked to do was to put all the “good” books I mentioned during the talk in a list that people can easily refer to.

The Central Message

The central message of my talk was very simple - there are plenty of programming books out there, but the majority of them are not worth your time and you’d probably not learn much from them. I tried to give everyone a simple recipe to spot great books and hone in on them. A good book:

  • ages very well
  • covers topics that are applicable in a broad spectrum of programming languages and frameworks
  • is written by practitioners (as opposed to academics/professional writers)
  • goes way beyond the basics and teaches you how to effectively use a certain technology
  • explains complex concepts in simple terms
  • challenges the way you think and pushes you to expand your mind

I made the case that in general no one should read more than 1 reference per some technology (and for frameworks even this might be too much) and that usually there are only a couple of references in a certain domain that are much better than everything else out there.

I’ve also tried to make the point that after a certain point it doesn’t pay off very well to dig deeper and deeper into the same subject matter, and that you should consider expanding your knowledge horizontally - e.g. if you’re an OOP expert you should start exploring functional or logical programming; if you’re a back-end developer you should learn about the front-end technologies and so on. Ultimately, ideas matter much more than any concrete implementation of those ideas.

The Reading List

It was probably not apparent in my talk, but I had picked some of my favourite books in all the areas that I consider essential for the success of a programmer - e.g. CS fundamentals, operating systems, effective usage of a language/technology stack, software design, etc. Just to be clear - you don’t really need to read a dozen books on CS fundamentals or operating systems. Furthermore - some of the books listed in each section cover more or less the same ground. Just pick whatever seems most interesting to you.

One really important thing to understand is that the list that follows is just a sampling of the type of books that any (good) programmer should read. This article (and the talk) could have easily been named “The Type of Books that Every Programmer should Read” or “The Makings of a Good Book on Programming”. Still, naming remains the hardest problem in our industry and some titles are more catchy than others. I’ve noticed that too many people focus on the fact that some particular technologies are mentioned (e.g. Unix, C and Ruby). Obviously not all developers should know C and Ruby, but when discussing what makes a good reference book some concrete examples are always useful.

Bellow is a listing of all the “good”3 books my talk, grouped by categories.

CS Fundamentals

Operating Systems

References

Note: Please ignore the specific technologies references here. My point was to simply list a few good reference books as a baseline for all of you, not to promote any particular languages.4 Still, I do believe that’s it valuable for everyone to gain some insight into the languages that shaped our industry (e.g. Lisp, C, Smalltalk), even if you’re never going to use them directly/professionally. Understanding the history and the circumstances that lead to some modern development is certainly going to make you a better programmer.

Effective Usage

Note: Again, please ignore the references to specific programming languages. They are here for illustrative purposes only.

Software Design

Productivity

Project Management

Meta

The Greatest Hits

I realize the list looks daunting and probably few people will have the time to go over all the books in it. Many of them are definitely not light bedside reading material. I have to admit there are some (many) books on the list that I didn’t finish myself. If I had to narrow down the list to the 3 most important (impactful) books there those would be:

  • Structure and Interpretation of Computer Programs
  • The Elements of Style
  • Code Complete
  • Thinking, Fast and Slow

Seems, I’ve made an off-by-one error, but you’ll forgive me.

The Sequel

There are many great books that I didn’t mention in my HackConf talk (mostly due to time constraints). I’ll guess I’ll have to do a follow-up blog post for them. Generally speaking, I wanted to give people an idea of what a quality book means and I was certain they’d be able uncover more of those. Teach a person to fish and all that jazz…

That being said, I wouldn’t mind following up on my original talk with a sequel that expands on the basic ideas I outlined there and refines the list of books presented. There were a couple of topics that I intentionally avoided (peopleware/soft skills, leadership), so there’s definitely some more ground to cover. Not to mention I read quite a few other great books since 2015. And I should probably do it in English next time!

Epilogue

I think this is the only blog post that I’ve planned to write for 5 years before it became a reality. Better late than never, right? It took me quite a while to get to it, but this gave me extra time to reflect on my talk. Sometimes even I manage to find a different message in my presentations after ruminating on them for a while.

I hope this post will inspire you to do some quality reading during the lockdown5 and introduce a bit of joy in your life. It’d be nice of something good came out of it. By the way, why don’t you share in the comments your favourite books? I’d love to get some reading ideas myself!

  1. Way back in 2015. 

  2. You can find a recording of the talk here. (it’s in Bulgarian) 

  3. In the talk I kept comparing some books that I consider “bad” to books that I consider “good”. 

  4. I’m actually quite fond of promoting Lisp(s) - it’s an amazing and rather underappreciated language. 

  5. As I write this in Spring 2020, much of the world’s population is under lockdown, because of the ongoing COVID-19 pandemic. 

-1:-- The Books That Every Programmer Should Read (Post Bozhidar Batsov)--L0--C0--March 27, 2020 07:26 AM

Meta Redux: Meta Reduce 2020.0: Lockdown

I haven’t written any blog posts in months and I guess it’s high time I changed this. I had promised myself last year that I’d do a lot of writing in 2020, but for one reason or another so far this hasn’t been the case. On one hand I was quite tired after the busy end of 2020, so I needed a bit of time-off from blogging and open-source, and on another - our lives have really changed in the past several weeks by the global coronavirus pandemic. It has been 2 weeks since the complete lockdown started in Bulgaria to fight COVID-19, and the isolation that goes with it. On the bright side - with little to do I’ve got more time for OSS and blogging.

So, what have I been up to recently?

IN/Clojure

In February I’ve travelled to India for the local IN/Clojure conference.1 The conference was really great and was very well organized. Most importantly - I met there so many smart, interesting, diverse, driven and fun people. And they were all passionate about Clojure and creating a better development experience for everyone!2 That’s the best part of every conference for me. Well, I also enjoyed the best Indian food ever around the conference and one truly legendary conference afterparty.3

I took the opportunity to release CIDER 0.24 (India) while I was at IN/Clojure. It’s dedicated to all of India and all the fine people I met there. I never found the time to write a proper release announcement about CIDER 0.24, but it’s a great releases and you definitely want to upgrade to it.

I had the good fortune to attend a couple of meetups as well, while in India - an FP meetup in Bangalore and an Emacs meetup in Pune. Believe it or not this was the very first time in my life I attended any kind of event dedicated to Emacs! That meetup was so much fun! It was particularly cool to be in the audience when Suvrat Apte delivered an awesome talk on CIDER’s debugger!

By the way - you can already find online the recording of my talk from IN/Clojure, titled “The Future of Clojure Tooling”.

The REPL Podcast

Last October I was a guest at The REPL - an awesome Clojure podcast hosted by Daniel Compton. The recording finally went live today. If you’re into CIDER, nREPL and Clojure development tools I guess you might enjoy listening to our conversation.

Major Projects

These days my biggest focus on the OSS front is shipping nREPL 0.7 and RuboCop 1.0. The nREPL release is right around the corner, as it is feature completely and mostly lacks a proper release announcement. RuboCop 1.0 requires a bit more work, but I hope to ship it sometime next month.

Once those are out of the way I’ll focus more on nREPL 1.0, CIDER 1.0, and everything else in CIDER’s Orchard “graduating” to stability.

Smaller Projects

Most of my smaller projects have been on ice lately, with clojure-mode being the only notable exception. I’ve merged and reviewed a bunch of patches there recently. A new release should happen relatively soon.

Meta

The most important thing, given the circumstances, is that so far me, my family and my friends have been spared by COVID-19. Bulgaria introduced the lockdown quickly and I hope this is going to help contain its spread, as our healthcare system is not exactly top notch. If the containment measures fail we’ll finally test empirically whether our national brandy rakiya can truly cure every decease in existence…

I’m fortunate enough to be working remotely, so little has changed in my day-to-day, other than all of the fun aspects of life disappearing all of a sudden. Still, I’m glad that “remote work” is trending these days and I hope that the crisis is going to enlighten many people about its virtues and help propel it to the mainstream. It’d be great if something good came out of this tragedy.

There’s not much to do for fun, besides watching some TV shows and reading some books. Currently I’m binging through “The Wire” and I’m really enjoying it. I’ve been planning to watch it for almost 15 years, so I’m quite happy I got to do this at least. On the reading front - last week I read the first “Witcher” book and it was a lot of fun. I certainly enjoyed it way more than the TV show. This week I’ve started “Sapiens: A Brief History of Mankind”, another way overdue item on my todo list.

I miss my “normal” life. The current situation is another reminder of how often you don’t appreciate the important things in life until you lose them.

That’s all for me for now. Stay safe and sane!

  1. That was my very first trip to India! 

  2. I also met quite a few people who were very passionate about Emacs. 

  3. Which I barely remember. 

-1:-- Meta Reduce 2020.0: Lockdown (Post Bozhidar Batsov)--L0--C0--March 25, 2020 11:14 AM

Bryan Murdock: Git Annex is Great


I'm developing the website for my business and I have a mix of code an images in my git repository. Since everyone seems to know that you shouldn't keep large binary files in your git repo, I decide to see what the current solutions to that problem are.

After doing a little bit of searching, I narrowed things down to git lfs and git annex. Git lfs looks so nice and simple, except I'm not using github. Sure, you can set up your own central git lfs server yourself, but that sounded suddenly not so nice and simple.

The website for git annex immediately hits you with all the power and flexibility that it has, and I was turned off by that complexity. I did like that it doesn't require any kind of central server for me to set up, so I didn't reject it outright. After some digging I found out that it does actually support a usage that looks a lot like git lfs, where you can configure it to automatically manage certain sets of files and then you just use git commands like normal. This is very nice. Here's how you set it up in your git repo (hopefully before you have committed any binary files to git, see my next blog post about fixing that):

git annex init
git annex config --set annex.largefiles 'mimeencoding=binary and largerthan=1b'

That's it! Now just use git commands like normal and annex will take care of binary files for you. The only tricky part to setting this up was figuring out that empty files, like those __init__.py files that Django creates, were considered binary files. That's why I had to add the and largerthan clause.

There are a couple of other things you might need to be aware of:

  • When you clone, none of the binary files will get copied to your clone until you run git annex get, and that will only copy over the files for the current commit. If you checkout an older commit or another branch, you might need to run git annex get again.
  • If you do start collaborating with others you'll have to make sure that their git annex get command can access the binary files. That's where you have many many options for setting up network communication that can work for your team. I have not delved into the details of that yet.
  • If you try to delete a clone, you'll discover that the annex files down under .git are read-only. Using sudo, or changing permissions with chmod will fix that.

Aside from those things I haven't noticed any trickiness with git annex. It seems like a great tool.

-1:-- Git Annex is Great (Post Bryan (noreply@blogger.com))--L0--C0--March 25, 2020 03:57 AM

Bryan Murdock: How To Retroactively Annex Files Already in a Git Repo


Table of Contents

How To Retroactively Annex Files Already in a Git Repo

In my last post I talked about how surprisingly easy it is to use git annex to manage your large binary files (or even small ones). In this post, I'm going to show how hard it is to go back and fix the mistake you made when you decided not to learn and use git annex at the start of your project. Learn from my mistake!

When I started developing the website for my business, I figured that editing history in git is easy, and I could just check in binary files (like the images) for now and fix it later. Well, it was starting to get a little sluggish, and I had some bigger binary files that I wanted to start keeping with the website code, so I figured the time had come. Once I decided on git annex, it was time to go edit that history.

First Tries: filter-branch, filter-repo

There is a very old page of instructions for doing this using git filter-branch. The first thing I noticed when I tried that was this message from git:

WARNING: git-filter-branch has a glut of gotchas generating mangled history
         rewrites.  Hit Ctrl-C before proceeding to abort, then use an
         alternative filtering tool such as 'git filter-repo'
         (https://github.com/newren/git-filter-repo/) instead.  See the
         filter-branch manual page for more details; to squelch this warning,
         set FILTER_BRANCH_SQUELCH_WARNING=1.

Yikes! A warning like that from a tool (git) that is already known for its gotchas is one I decided to take seriously. Besides, I'm always down to try the new hotness, so I started reading about git-filter-repo. The more I read and experimented, even dug into the source code, the more I came to understand that it could not do what I needed, sadly. Maybe someone will read this and correct me.

Success with git rebase –interactive

Not seeing a nice pre-built tool or command that could do this for me, I set out to manually edit the repository history using good ol' git rebase --interactive. First, I had to find the all the binary files that are in the repo (not just the ones in the current revision). Here's how I did it:

# The --stat=1000 is so it doesn't truncate anything
git log --stat=1000 | grep Bin | sort | uniq > binary-files

Note the comment. Isn't it cute that git log truncates long lines even when stdout is not connected to your terminal? There are lots of little annoying gotchas like that throughout this process. Makes me miss mercurial, but don't worry, I will try not to mention mercurial again.

Now, you'll still have duplicates in binary-files because the other stuff that git log --stat spits out on each line. I personally used some emacs commands to remove everything but the filename from each line of the binary-files file, and then did a sort and uniq again.

Next, I had to find each commit that modified any of these binary files. Here's how I did that:

for file in $(cat binary-files); do
    git log --pretty=oneline --follow -- $file >> commits;
 done

Then I did another sort and uniq on that. Luckily there were only about 15 commits. Phew.

Next I tried to find the earliest commit in the list I had, but that was a pain (don't…mention…mercurial…), so I just ran git rebase --interactive and gave it one of the first commits I made in the repository. I actually used emacs magit to start the rebase, but the surgery required throughout the process made me drop to the command-line for most of it. magit did make it really easy to mark the 15 commits from my commits file with an e though.

OK, once the rebase got rolling I ran into a few different scenarios. Commits that added a new binary file, commits that deleted binary files, commits that modified binary files, and a commit that moved binary files.

Added binary files

When a binary file was added, git would act like I have always seen rebase interactive work, it would show the normal thing:

Stopped at 53fc550...  some commit message here
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

In that case I did this:

git show --stat=1000 # to see binary (Bin) files
git rm --cached <the-binary-files>
git add <the-binary-files> # git annex will annex them
git commit --amend
git rebase --continue

Easy peasy, as long as you have set up annex like my previous post explains so that annexing happens automatically.

Deleted binary files

When a binary file was deleted, git would throw up a message like this up:

$ git rebase --continue
[detached HEAD 130bcc4] banner on each page now
 21 files changed, 190 insertions(+), 42 deletions(-)
 create mode 100644 msd/webshop/static/webshop/img/common/adi-goldstein-EUsVwEOsblE-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/alexandre-debieve-FO7JIlwjOtU-unsplash.jpg
 delete mode 100644 msd/webshop/static/webshop/img/common/file-icons.png
 create mode 100644 msd/webshop/static/webshop/img/common/kevin-ku-w7ZyuGYNpRQ-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/levi-saunders-1nz-KjRdg-s-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/max-duzij-qAjJk-un3BI-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/umberto-jXd2FSvcRr8-unsplash.jpg
 create mode 100644 msd/webshop/static/webshop/img/common/yogesh-phuyal-mjwGKmwkDDA-unsplash.jpg
CONFLICT (modify/delete): msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg deleted in 90d71fb... refactored banners in pricing.css to reduce code duplication and modified in HEAD. Version HEAD of msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg left in tree.
error: could not apply 90d71fb... refactored banners in pricing.css to reduce code duplication
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 90d71fb... refactored banners in pricing.css to reduce code duplication

I guess in this case it was that I had added some new files too, so the message was extra verbose. The key message in all that was: "msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg deleted…" Here's what you do in this case:

git rm msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg
git diff --stat=1000 --staged # to find full paths for any Bin files
git restore --staged <binary-files>
git add <binary-files>
git diff --stat --staged # just to double check there are no Bin files now
git rebase --continue

Looks so simple (heh), but it took me a decent amount of web searching and experimentation to figure it out. All for you, dear reader, all for you.

Modified binary files

Here's one where I resized several images, git helpfully uttered:

$ git rebase --continue
[detached HEAD 7dfb28c] refactored banners in pricing.css to reduce code duplication
 4 files changed, 28 insertions(+), 75 deletions(-)
 create mode 100644 msd/webshop/static/webshop/img/common/connor-betts-QK6Iwzd5MhE-unsplash.jpg
 delete mode 100644 msd/webshop/static/webshop/img/common/nick-fewings-ZJAnGFg-rM4-unsplash.jpg
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/yogesh-phuyal-mjwGKmwkDDA-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/umberto-jXd2FSvcRr8-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/max-duzij-qAjJk-un3BI-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/levi-saunders-1nz-KjRdg-s-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/kevin-ku-w7ZyuGYNpRQ-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/connor-betts-QK6Iwzd5MhE-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/alexandre-debieve-FO7JIlwjOtU-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
warning: Cannot merge binary files: msd/webshop/static/webshop/img/common/adi-goldstein-EUsVwEOsblE-unsplash.jpg (HEAD vs. a90710f... scaled images down to max width of 1920 pixels)
Auto-merging msd/webshop/static/webshop/img/common/yogesh-phuyal-mjwGKmwkDDA-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/yogesh-phuyal-mjwGKmwkDDA-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/umberto-jXd2FSvcRr8-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/umberto-jXd2FSvcRr8-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/max-duzij-qAjJk-un3BI-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/max-duzij-qAjJk-un3BI-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/levi-saunders-1nz-KjRdg-s-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/levi-saunders-1nz-KjRdg-s-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/kevin-ku-w7ZyuGYNpRQ-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/kevin-ku-w7ZyuGYNpRQ-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/connor-betts-QK6Iwzd5MhE-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/connor-betts-QK6Iwzd5MhE-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/alexandre-debieve-FO7JIlwjOtU-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/alexandre-debieve-FO7JIlwjOtU-unsplash.jpg
Auto-merging msd/webshop/static/webshop/img/common/adi-goldstein-EUsVwEOsblE-unsplash.jpg
CONFLICT (content): Merge conflict in msd/webshop/static/webshop/img/common/adi-goldstein-EUsVwEOsblE-unsplash.jpg
error: could not apply a90710f... scaled images down to max width of 1920 pixels
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply a90710f... scaled images down to max width of 1920 pixels

The trick to fixing this is to notice which commit it's trying to let you edit, which is in the last line of that message, and then checkout that version of each of the unmerged binary files it mentions, like so:

git status # to get the names of the unmerged binary files
git checkout a90710f <filenames>

Now you can do the same thing you did for the deleted file:

git restore --staged <filenames>
git add <filenames>
git diff --stat --staged # just to double check there are no Bin files now
git rebase --continue

Moved binary files

When I ran git log --follow to find all the commits that modified binary files, it flagged one where I had moved them. I'm not sure I actually had to edit that commit and I wonder if I would not have had this weird situation if I had not edited it. But for completeness, here's what I saw. Git rebase stopped to let me edit the commit and git annex printed out this message for every file that was moved:

git-annex: git status will show <filename> to be modified, since content availability has changed and git-annex was unable to update the index. This is only a cosmetic problem affecting git status; git add, git commit, etc won't be affected. To fix the git status display, you can run: git update-index -q --refresh <filename>

Sounds…quite weird. But git rebase would not continue until I did run the suggested command:

git update-index -q --refresh <filenames>
git rebase --continue

Dealing with Tags

Once the rebase was done I noticed that the tags I had all still pointed to the original commits. Oops. A quick internet search led me to this post about rebasing and moving tags to the new commits (written by a former co-worker, it just so happens). Too bad I didn't look for that before I rebased. I thought about redoing the whole rebase, but in the end I just wrote my own quick python script (using snippets from Nacho's) to take care of my specific situation. Here it is:

#! /usr/bin/env python
from subprocess import run, PIPE

tags = run(['git', 'show-ref', '--tags'],
           stdout=PIPE).stdout.decode('utf-8').splitlines()

tags_with_comments = {}
for tag in tags:
    tag_hash, tag_name = tag.split(' ')
    tag_name = tag_name.split('/')[-1]
    comment = run(['git', '--no-pager', 'show', '-s',
                   '--format=%s', tag_hash],
                  stdout=PIPE).stdout.decode('utf-8').splitlines()[-1]
    print(f'{tag_name}: {comment}')
    tags_with_comments[tag_name] = comment

commits = run(['git', 'log', '--oneline'],
              stdout=PIPE).stdout.decode('utf-8').splitlines()

for tag_name in tags_with_comments:
    for c in commits:
        commit_hash = c.split(' ')[0]
        comment = c.split(' ')[1:]
        comment = ' '.join(comment)
        if comment == tags_with_comments[tag_name]:
            run(['git', 'tag', '--force', tag_name, commit_hash])

Clean Up and Results

Well, with all that done, it was time to see how it all turned out. My original git repo was sitting at about 1.4 GB. This new repo was…3 GB!? Something wasn't right. Here are some steps I took to clean it up after making sure there weren't any old branches or remotes laying around:

git clean -fdx
git annex fsck
git fsck
git reflog expire --verbose --expire=0 --all
git gc --prune=0

The git clean command showed that I had a weird leftover .git directory in another directory somehow, so I deleted that. I don't think the fsck commands really did anything, but the gc definitely did. Size was now down to 985 MB. Much better. Wait a minute, what if I did a git gc on the original repo? It's size went down to 984 MB. Oh shoot. I guess it makes sense though, if both git and git annex are storing full versions of each binary file they would end up the same size. The real win is the faster git operations, especially clones.

A local git clone now happens in the blink of an eye, and its size is only 153 MB. Now, that's a little unfair because it doesn't have any of the binary files. After a git annex get to get the binary files for the current checkout it goes up to 943 MB. Not a huge savings, but it only gets better as time goes on and more edits happen. Right? This was all worth it, wasn't it?!

Let me know in the comments if this is helpful, hurtful, or if I did this totally wrong.

-1:-- How To Retroactively Annex Files Already in a Git Repo (Post Bryan (noreply@blogger.com))--L0--C0--March 25, 2020 03:55 AM

Sacha Chua: 2020-03-23 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file and emacs-devel.

-1:-- 2020-03-23 Emacs news (Post Sacha Chua)--L0--C0--March 24, 2020 03:36 AM

Manuel Uberti: Lockdown Beam: eldoc-eval

Lockdown Beam

As mentioned briefly at the end of the previous entry, today we are going to play with Emacs’ Eldoc. Sometimes I forget about it, but eldoc-mode is one of those subtle things which improve my daily Emacs routine. Besides giving me useful information in Elisp, eldoc-mode is always helpful when I am programming in Clojure thanks to its integration with CIDER.

However, there is another place where Eldoc comes in handy. You surely know that when you press M-: you can type an expression in the minibuffer and then evaluate it by pressing RET. But wouldn’t it be great to have Eldoc for these quick runs to?

The answer is not “Well, yes!”, but “Hey, there is a mode for it!”, which is what an Emacser regularly replies when asked about any matter whatsoever. This time the mode comes with the package of the day: Thierry Volpiatto’s eldoc-eval.

Once installed, activating eldoc-eval is easy. Turn on eldoc-in-minibuffer-mode and everything is set.

Nice and simple. You may not like the Eldoc information appearing in the mode-line, but Thierry got you covered with eldoc-in-minibuffer-show-fn. If you use tooltips, try setting this to #'tooltip-show. There are other features in eldoc-eval worth of notice, so be sure to check out its README if you want more juice.

As for me, I am happy with just enabling eldoc-in-minibuffer-mode. I patched eldoc-show-in-mode-line to fit the help message properly in my custom mode-line, but that’s it. If you are using your own mode-line too, I am leaving that as a little exercise1.

Next time we will meet a Git-related package which doesn’t include the letters “m” and “a” in its name.

Stay safe.

Notes

  1. I used el-patch for this. 

-1:-- Lockdown Beam: eldoc-eval (Post)--L0--C0--March 23, 2020 12:00 AM

Irreal: Zamansky 69: Floobits

One of the questions I often see is, “how can I collaborate in real time with Emacs?” This may be because a couple of programmers want to do some remote pair programming or because two or more people want to collaborate on a paper they’re writing with Org-mode. Until recently, I didn’t have a good answer—Magit, Emacs, blah blah blah—but the ever resourceful Mike Zamanksy has the answer in the latest video of his Using Emacs Series.

In the video, Using Emacs 69 Floobits, Zamansky demonstrates how to use Floobits. It works with Emacs, Atom, Neovim, and a couple of other editors. This is really good news for Emacs users because for the first time there’s a good solution for realtime collaboration.

Zamansky is, of course, a teacher so his interest is mainly in negotiating the learn-from-home regime necessitated by COVID-19 but useful though it is for teachers, Floobits is a boon to those of us outside of education. It’s a boon for the same reason it is for education: it allows two or more Emacs users to collaborate in real time. If nothing else, it’s an answer to those who say, “I have to use Google Docs so I can collaborate with my coauthors.”

Floobits is a bit like the early GitHub. You can get a free account but your workspaces are pubic. If you want private workspace, you have to buy a subscription but they’re not too expensive and well worth the expense if you really need the live collaboration.

The video is 15 minutes, 43 seconds so plan accordingly. If you have any interest at all in realtime collaboration and you’re an Emacs user, you’ll find it well worth your while.

-1:-- Zamansky 69: Floobits (Post jcs)--L0--C0--March 22, 2020 04:39 PM

Irreal: Migrating from Emacs to Doom

Jethro Kuan, whom I wrote about in regards to his work with smart notes, has another interesting post. This time it’s about his migration to Doom Emacs. There’s nothing unusual about that of course; I’ve written several posts about such migrations. What’s a (little bit) different about Kuan’s journey is that he started from vanilla Emacs instead of Vim like most such immigrants.

Of course, many Emacs users have started using Spacemacs or Doom because of RSI issues. The attraction for them is the Vim keybindings, which most people find easier on their wrists and fingers. That was not Kuan’s motivation, though. He turned off evil mode and is still using the normal Emacs keybindings.

Kuan is interested in simplifying his configuration and using as much of the built-in Emacs functionality as possible. He says that Doom is faster and more responsive than vanilla Emacs and loads faster. I’ve never understood the obsession with Emacs’ load speed. After all, most of us just leave it running—at least in server mode–all the time so a few seconds of load time in the morning doesn’t amount to much. I was a bit surprised that Doom is more responsive. It is, after all, just a prebuilt Emacs configuration. It’s probably a matter of Doom getting things like load order right.

In any event, if you feel like experimenting with your editor, trying Doom may be more advantageous then wasting your time on one of those other editors. Kuan says it took him about 2 hours to complete the migration and then he spent another hour tweaking it to his liking so it’s reasonably low cost to experiment if you feel inclined.

-1:-- Migrating from Emacs to Doom (Post jcs)--L0--C0--March 21, 2020 03:27 PM

Mike Zamansky: Using Emacs 69 Floobits

Hunter, like most other schools has gone remote. I taught my first two online classes on Thursday. Currently, I'm using Zoom for synchronous stuff and a mailing list and slack for async. There are still some missing pieces. When we're all together, it's easy to look at a student's work and talk them through issues. It's also easy to get students to work together, at least to a point. With everyone locked up in their own homes, real time collaboration is harder.
-1:-- Using Emacs 69 Floobits (Post)--L0--C0--March 21, 2020 02:52 PM

Alvaro Ramirez: Modern Emacs lisp libraries

21 March 2020 Modern Emacs lisp libraries

Quickly finding related built-in elisp functions (without prefixes) can sometimes take a little poking around.

Some modern and predictable built-in exceptions I now reach out to are:

  • map.el for key/values, alists, hash-tables and arrays (built-in as of Emacs 25.1).
  • seq.el for alist, hash-tables and array manipulation (built-in as of Emacs 25.1).
  • subr-x.el has a handful of string functions (built-in as of Emacs 24.4).
  • let-alist.el wonderful syntax for alists, great for json (built-in as of Emacs 25.1).

If you don't mind reaching out to third-party libs (you likely have some of these already installed), here are some modern, predictable, and well-documented ones that always get me out of trouble:

I'm happy with built-ins like map.el, seq.el, and let-alist.el. subr-x.el is also pretty nice, although not as full-featured as third-party s.el.

Am I missing out on other modern built-ins or third-party libraries?

UPDATE: Added a handful of newly discovered libraries plus suggestions by Daniel Martín (thanks!). Not tried any of these myself.

comments on twitter

-1:-- Modern Emacs lisp libraries (Post Álvaro Ramírez)--L0--C0--March 21, 2020 12:00 AM

Raimon Grau: TRAMP with docker is awesome

There's this recent post in reddit where the author shows how to chain tramp connections. And it's great indeed!

What I'm using TRAMP more often for is to login to containers.

Using docker-tramp, I usually browse into a docker and start dired there. But then, I can open a shell (or eshell if there's no bash) there.

And, for example, if you run a postgres container, why not opening a `sql-postgres` buffer?.

Just:

docker run -e POSTGRES_PASSWORD=a -e POSTGRES_USER=rgrau -ti --rm postgres

and when dired-ing through tramp, m-x sql-postrgres using the appropriate params.  Tada!

Neat, ain't it?

Happy hacking, and stay safe.
-1:-- TRAMP with docker is awesome (Post Raimon Grau (noreply@blogger.com))--L0--C0--March 20, 2020 05:42 PM

Alvaro Ramirez: Emacs smartparens auto-indent

20 March 2020 Emacs smartparens auto-indent

While I do most editing in Emacs, I use Xcode every now and then. I like Xcode's pair matching (of brackets) combined with its auto-indent.

xcode_indent.gif

While the wonderful smartparens gives Emacs pair-matching powers, it doesn't automatically indent between pairs (out of the box anyway).

indent_bracket_before.gif

Luckily, smartparens does provide sp-local-pair, which enables us to achieve a similar goal.

With a short snippet, we can autoindent between {}, [], and () when pressing return in-between.

( defun  indent-between-pair ( &rest _ignored)
  (newline)
  (indent-according-to-mode)
  (forward-line -1)
  (indent-according-to-mode))

(sp-local-pair 'prog-mode  "{" nil  :post-handlers '((indent-between-pair  "RET")))
(sp-local-pair 'prog-mode  "[" nil  :post-handlers '((indent-between-pair  "RET")))
(sp-local-pair 'prog-mode  "(" nil  :post-handlers '((indent-between-pair  "RET")))

indent_bracket_after.gif

comments on twitter

-1:-- Emacs smartparens auto-indent (Post Álvaro Ramírez)--L0--C0--March 20, 2020 12:00 AM

Irreal: Interactive SQL Queries With Org-mode

Marcin Borkowski (mbork) always has interesting posts that often give me good ideas. Recently, he posted on using Org-mode for SQL. I haven’t had to deal with SQL for a long time and I was never a heavy user so any complicated query always took me multiple tries. If, like me, you’re an interactive programming aficionado, what you’d like to do is to be able to build up your query piece by piece. SQL certainly doesn’t lend itself to interactive programming but Borkowski found a way. Actually, it’s pretty obvious once you see it.

His idea is to execute the query from inside an Org Babel block. He can continually execute and refine the query until he gets what he wants. The nice thing is that your last attempt doesn’t go away. It’s still there to edit and try again.

Take a look at Borkowski’s post for the details and a worked example. The post is worth reading if only to see his definition of “toy editor.”

-1:-- Interactive SQL Queries With Org-mode (Post jcs)--L0--C0--March 19, 2020 03:34 PM

Manuel Uberti: Lockdown Beam: bm.el

Lockdown Beam

I guess there is no need to explain the choice of the word “lockdown” in the title. I don’t know about you, but here in Italy things are getting worse and worse. Every time I look outside the window I see desolation, and for a George Romero fan this is getting scarier and scarier. I am lucky, though. I can work from home (thank you, 7bridges) and study from home (thank you, Ca’ Foscari). But I can also take the time to spread words of love about some Emacs packages I rarely talk about.

Lockdown Beam aims to be a series of small articles on packages that deserve more attention, but I can’t tell now how long this series is going to run. I tend to use all the packages configured in my init.el, and most of them are fairly popular, so it’s not like digging obscure entries from the dust of my setup. However, out of more than 100 packages, I am pretty sure I can find a decent amount of poor neglected things in there.

Let’s start with Jo Odland’s bm.el.

There are many great ways to bookmark things in Emacs1, but sometimes a visual clue just adds a good feeling of immediacy one (e.g., myself) can find pleasant. The README says this is a feature from Visual Studio that the author was missing in Emacs, but you will have to check for yourself whether this is something Visual Studio does. You know there is no such thing as a different text editor for me.

Following the documentation, setting up bm.el is easy. If you use use-package, there a is nice example ready for copy-and-paste at Configuring bm.el with use-package. I started from there, but I replaced the hook on vc-before-checkin-hook with a hook on magit-pre-refresh-hook.

I find bm.el really useful when studying source code from others, or when I want to quickly set jumping points in a log file cluttered with stacktraces. It’s quicker then moving around with Isearch or helm-occur, and unlike avy it’s not limited to what is currently visible on the screen.

That’s all for today. Next time a little friend will help me exploit Eldoc.

Stay safe.

Notes

  1. Check out what Protesilaos Stavrou has to say about registers

-1:-- Lockdown Beam: bm.el (Post)--L0--C0--March 19, 2020 12:00 AM

Irreal: Elfeed and Goto-address

The other day I was reading an Elfeed entry and wanted to follow a link so with the point on the link, I typed Ctrl+c Ctrl+o as usual. Nothing happened. In Elfeed you can click on a link or press Return when the point is on it to follow a link so it wasn’t much of an inconvenience but then I vaguely remembered that I’d done something to make Ctrl+c Ctrl+o work in non-Org buffers.

I browsed through my init.el file until I found the code and used it to find the blog post I’d written about it. Looking at the code, it was immediately clear why it wasn’t working: you have to specify which modes you want it to work in. I just added the mode that Elfeed uses for viewing stories and everything worked fine. The necessary mode to enable is elfeed-show-mode.

In case you’re in similar predicament, here’s the code I’m using. As I mention in the post, goto-address-mode is built in so despite the use-package there’s no package to load.

(use-package goto-addr
  :hook ((compilation-mode . goto-address-mode)
         (prog-mode . goto-address-prog-mode)
         (elfeed-show-mode . goto-address-mode)
         (eshell-mode . goto-address-mode)
         (shell-mode . goto-address-mode))
  :bind (:map goto-address-highlight-keymap
              ("C-c C-o" . goto-address-at-point))
  :commands (goto-address-prog-mode
             goto-address-mode))
-1:-- Elfeed and Goto-address (Post jcs)--L0--C0--March 17, 2020 03:46 PM

Sacha Chua: 2020-03-16 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file and emacs-devel.

-1:-- 2020-03-16 Emacs news (Post Sacha Chua)--L0--C0--March 17, 2020 12:08 AM

Irreal: Zamansky 68: Tramp and Org-publish

Mike Zamansky is back with another video in his Using Emacs Series. This video discusses maintaining a simple Web site with Emacs and Org-mode. Usually when I write about this sort of thing, it’s in the context of publishing to a blog. Zamansky’s need is a bit different. He has a couple of Web sites he maintains that support some of his programs at Hunter College. These are simple sites that Zamansky had been writing directly in HTML by logging into the host machine and editing the file in situ.

Then he realized that rather than logging onto the host machine to edit the files, he could use Tramp and do his editing from the comfort of his own environment. That’s already an improvement but then he wondered why he was going through the pain of writing in HTML when we could write in Org-mode and export to HTML. The default look of the HTML exported from Org isn’t very pretty but it’s easy to disable the default CSS and add your own. You can even embed custom CSS in the Org source file if you need something special for that page.

Zamansky’s final realization was that rather than export to HTML and then transfer the HTML to the host machine, he could simply use Org-publish and have the whole process automated. All he needed to do was write the Org source and call Org-publish to export it to HTML and transfer it to the host machine. His solution is much like Sachin Patil’s that I wrote about last Friday. It’s a nice work flow that eliminates the boring rote work and lets him concentrate on the site’s content.

Follow the link to the video for more details on his set up and how he arrived at his solution. The video runs 16 minutes, 20 seconds so plan accordingly.

-1:-- Zamansky 68: Tramp and Org-publish (Post jcs)--L0--C0--March 16, 2020 04:17 PM

Manuel Uberti: Light is right, alright?

For a long, long while I was a happy Solarized Light user. Then, somehow, I switched to Steve Purcell’s version of Tomorrow Night, and no other theme has been able to take me away from that colour scheme. There were times where I found myself looking for a new light theme, but the lack of a pleasant one1 left me in the comfortable palettes of Tomorrow Night. I even put on MELPA what at first looked like the light I needed, but the dark side still kept me within its reach.

Why didn’t I go back to Solarized Light if the experience had been so joyful? I guess getting older distanced me somehow from the pleasures of a yellowish background. Don’t get fooled by my grumpiness, though. It’s still one of the best Emacs themes out there, so be sure to check it out if you are after a light theme.

As for me, I’ve recently seen the light again. Protesilaos Stavrou open sourced a couple of beautiful themes, modus-vivendi and modus-operandi, both available at modus-themes, on ELPA, and on MELPA. I am sure the apt readers remember that modus-vivendi first appeared on these pages right at the end of Ripgrepping with Helm: the mode-line, but since then modus-operandi has become the default.

What I found intriguing in Protesilaos’ work is the reasoning behind the themes. I have never cared about the Web Content Accessibility Guidelines, which is a shame considering how poor my eyesight has been since I was a kid. The Modus Themes are built with those guidelines in mind and it shows immediately. The text is always readable, it doesn’t matter what face is applied over it. It’s not only a matter of being able to read, but to always distinguish specific contexts. For instance, comments are clearly separated from the rest of the code without sacrificing legibility. I know this sounds reasonable and it may already be the case for the theme you are currently using, but for my eyes it has never been so easy. And yes, I did tend to prefer beauty over accessibility.

Another remarkable thing about the Modus Themes is that both variants cover a wide range of Emacs packages, something not so axiomatic in the Emacs theme world. Admittedly, I bothered Protesilaos with some issues about Helm and few other packages, but he quickly tamed my wild sensibility. “If a compromise is ever necessary between aesthetics and accessibility, it shall always be made in the interest of latter”, the README points out. So far no complaints from the aesthetics department, though.

Protesilaos explained his ideas in My accessible Emacs themes, a valuable presentation which you should watch before My Modus Themes are in ELPA. Considering that I came from years with a dark background, the switch to a light one has been less traumatic than it may seem. It could be due to the fact that Solarized Light was there way before the dark age, why not? Nevertheless, the new colour scheme is as simple and beautiful as it is effective. See you around, Sith Lord.

Notes

  1. An obvious matter of personal taste. 

-1:-- Light is right, alright? (Post)--L0--C0--March 16, 2020 12:00 AM

Mike Zamansky: Using Emacs Episode 68 - Tramp and org-publish

I maintain a couple of small simple web sites. One provides information about my undergraduate honors CS program and another that isn't live yet is a FAQ for my CS teacher certification program. Traditionally I would use ssh to connect to the host machine, fire up Emacs and edit the html files to update the sites. I always forget that with Emacs we can do better. One way is with Tramp Mode.
-1:-- Using Emacs Episode 68 - Tramp and org-publish (Post)--L0--C0--March 15, 2020 12:57 AM

Jethro: Migrating To Doom Emacs

Yesterday, I spent some time migrating my vanilla Emacs configuration to Doom Emacs. The whole process took me about 2 hours, and then today I spent another hour or so figuring out why my original configurations felt different, and tweaked it back to my liking. For reference, here is my original .emacs.d (now archived), and my current configuration lives in my home git repository. Why Doom Emacs? Why did I bother?
-1:-- Migrating To Doom Emacs (Post)--L0--C0--March 14, 2020 04:00 PM

Irreal: Blogging With (Only) Emacs

If you’re an Emacser and a blogger, you will, of course, want to write and publish your posts from within Emacs. If you’re using WordPress, org2blog provides an excellent solution. You write your posts in Emacs and Org-mode as usual and push a couple of keys to publish them to WordPress. Of course, not everyone likes WordPress and may prefer to maintain a website with static posts. In many ways that’s a lot simpler and if you don’t have complex requirements may be the best solution.

Over at Opensource.com, Sachin Patil has a nice post that explores publishing a blog completely from within Emacs. There are plenty of solutions that are sort of like this that use Django, Hugo, or Jekyll but Patil wanted a solution that required only Emacs. Not surprisingly, Emacs has a solution for that. The solution is to write your posts in Org-mode, leveraging all its formatting capabilities, and then use Org-publish to convert the posts to HTML and push then to the server.

There are ways to include and publish a site CSS file to make your site your own. Patil covers all this in his post. If you’re looking to start a blog and would like to handle everything in Emacs, take a look at Patil’s post. It’s amazing how much one can accomplish with just Org-publish. The nice thing about this approach is that you have total control over how you publish to your site and what the end result looks like.

-1:-- Blogging With (Only) Emacs (Post jcs)--L0--C0--March 13, 2020 05:11 PM

Manuel Uberti: A better approach at searching with Helm

When I wrote about how I use ripgrep with Helm in Ripgrepping with Helm, I overlooked something. Actually, let’s be honest. I didn’t study my usual search habits like I should’ve had to.

Looking closely at my per-project searches, there are two patterns I tend to follow:

  • searching for the thing at point in the current project;
  • searching for something in the current project.

The first pattern is somehow addressed by the previous incarnation of mu-helm-rg, but it can be improved by leveraging helm’s :input option1.

For the second pattern helm-sources-using-default-as-input needs to be temporarily disabled, otherwise helm would start with the thing at point as :default and would run an initial search that most of the times is not what I need. I can still pick up the thing at point with M-n if needed, but in this case I prefer typing what I want to find.

To achieve this, I decided to reimplement helm-grep-ag-1 according to my needs:

(defun mu--helm-rg (directory &optional with-tap type)
  "Build the Helm command for `mu-helm-rg'.

For DIRECTORY, WITH-TAP, and TYPE see `mu-helm-rg'. This command
disables `helm-sources-using-default-as-input' temporarily to
avoid the automatic search which starts when :default is set to
`thing-at-point' (the default behaviour). The search starts
automatically only with WITH-TAP."
  (let ((helm-sources-using-default-as-input nil)
        (command (helm-grep--ag-command))
        (input (when with-tap
                 (thing-at-point 'sexp t))))
    (setq helm-source-grep-ag
          (helm-make-source (upcase command) 'helm-grep-ag-class
            :header-name (lambda (name)
                           (format "%s in %s"
                                   name (abbreviate-file-name directory)))
            :candidates-process (lambda ()
                                  (helm-grep-ag-init directory type))))
    (helm-set-local-variable 'helm-input-idle-delay helm-grep-input-idle-delay)
    (helm :sources 'helm-source-grep-ag
          :keymap helm-grep-map
          :history 'helm-grep-ag-history
          :input input
          :truncate-lines helm-grep-truncate-lines
          :buffer (format "*helm %s*" command))))

Now mu-helm-rg must be updated, but the changes are trivial. I only need to consider a new parameter, with-tap, and call mu--helm-rg properly.

(defun mu-helm-rg (directory &optional with-tap with-types)
  "Search in DIRECTORY with RG.

With WITH-TAP, search for thing at point. With WITH-TYPES, ask
for file types to search in."
  (interactive "P")
  (require 'helm-adaptive)
  (mu--helm-rg (expand-file-name directory)
               with-tap
               (helm-aif (and with-types
                              (helm-grep-ag-get-types))
                   (helm-comp-read
                    "RG type: " it
                    :must-match t
                    :marked-candidates t
                    :fc-transformer 'helm-adaptive-sort
                    :buffer "*helm rg types*"))))

Finally, let’s see the new mu-helm-rg in action:

(defun mu-helm-project-search (&optional with-types)
  (interactive "P")
  (mu-helm-rg (mu--project-root) nil with-types))

(defun mu-helm-project-search-at-point (&optional with-types)
  (interactive "P")
  (mu-helm-rg (mu--project-root) t with-types))

(defun mu-helm-file-search (&optional with-types)
  (interactive "P")
  (mu-helm-rg default-directory nil with-types))

Notes

  1. See the Developing section of the Helm wiki for more on this. 

-1:-- A better approach at searching with Helm (Post)--L0--C0--March 13, 2020 12:00 AM

Irreal: Smart Notes

I’m currently reading Sönke Ahrens’ How to Take Smart Notes, a book that describes Niklas Luhmann’s method of taking, storing, and retrieving notes about ideas he thought could be useful later. Luhmann kept his notes on what amounted to index cards and stored them in a wooden box. The idea, Zettelkasten in German, is usually translated as “slip-box” in English. I first came across the idea in this review and summary of Ahrens’ book by Tiago Forte.

Implementing Luhmann’s idea with physical index cards is still possible, of course, but most of us would prefer to use digital methods. There’s a whole site, Zettelkasten, supporting the idea as well as commercial applications such as Roam to help you implement the system.

As soon as I heard about the idea, I thought it would be an ideal application for Emacs and Org-mode. That’s hardly an original insight, of course, and a quick search yielded two excellent posts that discuss it. The first, by Jethro Kuan, describes his note taking work flow using the org-roam package that he wrote as a way to replicate Roam with Org-mode. Most of the post is a sort of précis of the ideas presented in Ahrens’ book.

The other post, How to Make Yourself Into a Learning Machine, describes the system used by Simon Eskildsen, Director of Production Engineering at Shopify. Eskildsen doesn’t use Org—he’s a Vimer—but he’s built his own Zettelkasten. I liked his post because he gives examples of actual notes and how he links them together.

Kuan points to a nice video by Nat Eliason that shows how he uses his (Roam-based) Zettelkasten to pull together an outline for an article he’s writing. It’s very enlightening and shows the power of the Zettelkasten method. It’s 15 minutes, 12 seconds long and definitely worth watching if you’re interested in the Zettelkasten idea.

I’m waiting to finish Ahrens’ book before I try my hand at using the method. It seems pretty straightforward to implement in Org but Kuan found he needed more so I’ll take a closer look at org-roam before I commit any serious effort to the project.

-1:-- Smart Notes (Post jcs)--L0--C0--March 12, 2020 05:41 PM

Tory Anderson: Scratch buffer

I have been deep in emacs for over a decade but still never utilized the scratch buffer until I saw how much it was part of the workflow of a friend. I’ve now fixed may ways and just needed a fast way to open it, optionally in a side Window. I wrote up this quick command and bound it to a convenient hydra key and now I have one-stroke scratch buffer opening.
-1:-- Scratch buffer (Post)--L0--C0--March 12, 2020 12:00 AM

Irreal: Org and Anki

If you took my advice and checked out Ali Abdaal’s videos on evidence-based study techniques, you know that one of the two guiding principles of effective learning is spaced repetition. An easy way to do that is to start with flashcards and use some sort of record keeping to track which questions you need to revisit. There are, of course, some tools available to automate this. One such tool is Anki. You can enter flashcards with a question on the front and an answer on the back and Anki will track your progress and help you review the questions you get wrong more often then the ones you get right.

On the other hand, who wants to deal with inputting questions and answers into some suboptimal, bespoke editor? Fortunately, you can get all the benefits of Anki and still do your editing in Emacs. Cheong Yiufung has an informative post that explains how to write your flashcards in Emacs and Org-mode and export them to Anki. The post has several videos—sadly without useful audio—that demonstrate the app in action.

The nice thing about Anki is that it’s functionally like a physical set of flashcards in that you can sync your deck across all your devices and take it with you on your phone or tablet. That’s perfect for, say, your bus or train ride: you can review your material in those short otherwise lost time spans.

The takeaways from this post are (1) you should be using spaced repetition to help you learn new material and (2) if you want to do spaced repetition with flashcards, Anki and Emacs is a good solution.

-1:-- Org and Anki (Post jcs)--L0--C0--March 11, 2020 04:41 PM

Sacha Chua: 2020-03-09 Emacs news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file and emacs-devel.

-1:-- 2020-03-09 Emacs news (Post Sacha Chua)--L0--C0--March 10, 2020 01:58 AM

Cyberthal: Textmind "Sprint" renamed to "Ramble"

A ramble goes further than a sprint.

Why the tortoise beat the hare: plodding pace > fast flair.

I've been calling a level-one heading in the daylog a "sprint", but that's wrong because sprint already means something else.

So I need a new name for the concept. For a while I tried calling them "walks", but that feels ambiguous and weak. Dogs get taken on walks. They are sterile minibreaks in suburbia.

The task to execute the "sprint" rename just reached the top of the todo list, so I need to make a decision.

I turned to the thesaurus for inspiration, querying "jaunt", which led me to "ramble", which is perfect. It encourages the relaxed feeling Textmind aims for, and emphasizes the disorganized nature of the chronological log. This aids lateral creative thinking.

There's no need to apply severe stressful internal focus, because Textmind already supplies that. What Emacs can't do is be creative, so the human user has to supply that by being in an open mood.

So whenever the user creates a new ramble, he titles the parent heading "Rambling [datestamp]".

Humans are hunter gatherers, and they are designed to wander through the woods and notice and act upon valuable information. Textmind essentially turns plain text into a forest through which the user can accomplish all his life objectives by merely rambling along.

I can already feel this rename putting me in a better frame of mind about working in Textmind! Visualizing my workflow as a forest ramble gives me a sense of enjoyment and progress that I otherwise can't find in the process itself.

Try working to this:

-1:-- Textmind "Sprint" renamed to "Ramble" (Post Cyberthal)--L0--C0--March 09, 2020 08:12 PM

Chen Bin (redguardtoo): How to speed up lsp-mode

Here is my full setup,

(eval-after-load 'lsp-mode
  '(progn
     ;; enable log only for debug
     (setq lsp-log-io nil)

     ;; use `evil-matchit' instead
     (setq lsp-enable-folding nil)

     ;; no real time syntax check
     (setq lsp-diagnostic-package :none)

     ;; handle yasnippet by myself
     (setq lsp-enable-snippet nil)

     ;; use `company-ctags' only.
     ;; Please note `company-lsp' is automatically enabled if installed
     (setq lsp-enable-completion-at-point nil)

     ;; turn off for better performance
     (setq lsp-enable-symbol-highlighting nil)

     ;; use ffip instead
     (setq lsp-enable-links nil)

     ;; auto restart lsp
     (setq lsp-restart 'auto-restart)

     ;; @see https://github.com/emacs-lsp/lsp-mode/pull/1498 and code related to auto configure.
     ;; Require clients could be slow.
     ;; I only load `lsp-clients' because it includes the js client which I'm interested
     (setq lsp-client-packages '(lsp-clients))

     ;; don't scan 3rd party javascript libraries
     (push "[/\\\\][^/\\\\]*\\.\\(json\\|html\\|jade\\)$" lsp-file-watch-ignored) ; json

     ;; don't ping LSP lanaguage server too frequently
     (defvar lsp-on-touch-time 0)
     (defadvice lsp-on-change (around lsp-on-change-hack activate)
       ;; don't run `lsp-on-change' too frequently
       (when (> (- (float-time (current-time))
                   lsp-on-touch-time) 30) ;; 30 seconds
         (setq lsp-on-touch-time (float-time (current-time)))
         ad-do-it))))

(defun my-connect-lsp (&optional no-reconnect)
  "Connect lsp server.  If NO-RECONNECT is t, don't shutdown existing lsp connection."
  (interactive "P")
  (when (and (not no-reconnect)
             (fboundp 'lsp-disconnect))
    (lsp-disconnect))
  (when (and buffer-file-name
             (not (member (file-name-extension buffer-file-name)
                          '("json"))))
    (unless (and (boundp 'lsp-mode) lsp-mode)
      (if (derived-mode-p 'js2-mode) (setq-local lsp-enable-imenu nil))
      (lsp-deferred))))

To enable lsp for the major mode XXX-mode needs only one line,

(add-hook 'XXX-mode-hook #'my-connect-lsp)

You also need install three packages,

Explanation,

Ctags is used to generate tags file for company-ctags and counsel-etags. GNU Find is required for find-file-in-project.

These three packages are faster and can replace the corresponding functionalities in lsp-mode.

I don't need any lint tools from lsp-mode because the lint tool is already included in our build script. I can see the syntax error from terminal.

I advice the lsp-on-change in order to notify the language server less frequently.

js2-mode has its own javascript parser extract imenu items. So I don't need javascript language server's parser to send back imenu items.

By default lsp-client-packages contains many clients, but I only code in javascript which is included in lsp-clients.

Here is code quoted from lsp-mode,

;;;###autoload
(defun lsp (&optional arg)
  ;; ...
  (when (and lsp-auto-configure)
    (seq-do (lambda (package) (require package nil t))
            lsp-client-packages))
  ;; ...
)

I have done some profiling by insert (profiler-report-cpu) at the end of lsp (the bottlenecks is highlighted).

lsp-mode-bottleneck-nq8.png

The language server I used can read jsconfig.json in project root. I can specify the directories to exclude in it.

-1:-- How to speed up lsp-mode (Post Chen Bin)--L0--C0--March 09, 2020 12:10 PM

Marcin Borkowski: Using Org-mode as a PostgreSQL client

I often have to write more or less complex SQL queries. In case of the easy ones, there is no problem – I just write them and that’s it. With more complex queries, however, it is very useful to adopt an incremental technique – writing them piece by piece. One of the best tools for such “incremental programming” is of course the REPL. In case of SQL (PostgreSQL in my case), this is usually solved with psql. While it has lots of nice features, editing a multiline query within it is not the nicest experience in the world. My first attempt to solving this problem was to use psql​‘s \e command. If given in the line on its own, it launches the default system editor with the previous query. While the readline library used by psql is fine, it is not very helpful in case of long, multiline queries – in such a case, a real editor like Emacs or Vim (or even a toy editor like everything else;-)) seems much better. After some time, however, I had an even better idea.
-1:-- Using Org-mode as a PostgreSQL client (Post)--L0--C0--March 09, 2020 08:12 AM

Irreal: Notion and Org Mode

Ali Abdaal, whom I’ve mentioned a couple of times before, is a physician and YouTuber from Britain. He’s a geek who at least once a week uploads a video usually about medicine, tech, or studying and learning. If you’re a student, you should definitely take a look at his videos on Evidence-Based Study Tips in which he discusses the scientifically-determined best ways of learning new material.

In a recent video he discusses the best note-taking app for students. That app is Notion, which Abdaal has increasingly been using to organize his life. As you can see from the video, Notion is, indeed, a very nice app that has many ways to organize and display your data and make it available across multiple devices. The problem with it is that it violates the important principle that you must maintain control of your data. With Notion, your data is held on the company’s servers and not stored locally. For that reason alone I don’t consider Notion a viable solution and I couldn’t recommend it to others for the same reason.

Still, Notion is an obviously useful and flexible app as you can see from Abdaal’s video. Fortunately, Emacs users can have essentially the same features that Notion offers while maintaining control of their data. Any Org Mode user watching the Abdaal’s video will immediately recognize the almost one-to-one correspondence between the ways Org and Notion operate. To be sure, Notion has a pretty GUI and may have capabilities that Abdaal hasn’t mentioned but as I’ve mentioned before, you can do the same things the video demonstrates with Org mode.

If you’re an Org mode user, take a look at the video and see if you don’t agree. The video is 24 minutes, 12 seconds so plan accordingly.

-1:-- Notion and Org Mode (Post jcs)--L0--C0--March 08, 2020 02:45 PM