import { OK } from '../global';
/**
* A Lambda Callback signals to AWS Lambda that the function has completed
* either successfully or otherwise. It can contain a result body (as a string)
* or throw an [Error object]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error}.
*
* @callback lambdaCallback
* @param {null|Error} res - The result of the function being called
* @param {string} [body=""] - A string response payload
*/
/**
* This default base class allows all extending events to inherit common
* functionality.
* @constructor LambdaEvent
*/
export default class {
/**
* A new instance of LambdaEvent
*
* @param {object} event - The event being received from AWS Lambda. The content of this object is
* determined greatly by the stream it has originated from.
* @param {string} event.Records - The records being processed by this Lambda function.
* @param {object} context - The context of the event being received
* [See here]{@link http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html}
* @param {lambdaCallback} cb - The callback function passed through from Lambda
*
* @see {@link http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-ddb-update|Sample DynamoDB Event}
*/
constructor(event, context, cb) {
/**
* The raw event received by Lambda
* @member {Object} LambdaEvent#event
*/
this.event = event;
/**
* The raw context object received by Lambda
* @member {Object} LambdaEvent#context
*/
this.context = context;
/**
* The response object allowing any extending class
* to respond to Cloudformation.
* @member {Function} LambdaEvent#cb
*/
this.cb = cb;
}
/**
* A Class-wrapper, allowing any class to handle and process events
* in a similar manner, without making assumptions on how the function
* will be executed.
*
* @function {Function} wrap
* @memberof LambdaEvent
* @static
*
* @param {Class} Req - the class that will handle the lambda event
* @param {...*} params - Any accompanying params that will be passed into
* the extending classes perform method.
*/
static wrap(Req, ...params) {
return (ev, ctx, fn) => { new Req(ev, ctx, fn).perform(...params); };
}
perform() {
throw new Error('missing #perform implementation');
}
respond(status = OK, body = '') {
if (status === OK) {
return this.cb(null, body);
} else {
return this.cb(new Error(`[500] "${body}"`));
}
}
}


Spotify
Youtube
Tidal
Apple-Music
Itunes
Deezer
Amazon-Music
Audiomack
TuneIn
Google-play
Napster
I Heart Radio
Wix Music
This Christmas Musical track is very simple and minimal instruments ensemble with soft melody and fun vibes for kids.Base of the music is piano chords progression and composed melodic sounds of string instruments with various key notes.
ℜ
©Dr Rahul Vaghela Music ™
℗Rahul Shailesh Vaghela


Code Coverage |
|||||||||
Lines |
Functions and Methods |
Classes and Traits |
|||||||
| Total | |
95.38% |
248 / 260 |
|
93.90% |
77 / 82 |
|
87.18% |
34 / 39 |
| Console | |
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
| Exceptions | |
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
| Facades | |
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
| Forms | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
| Http | |
91.53% |
54 / 59 |
|
90.00% |
18 / 20 |
|
81.82% |
9 / 11 |
| Interactions | |
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
| Providers | |
90.48% |
38 / 42 |
|
86.67% |
13 / 15 |
|
80.00% |
8 / 10 |
| Repositories | |
100.00% |
33 / 33 |
|
100.00% |
18 / 18 |
|
100.00% |
3 / 3 |
| Resources | |
100.00% |
21 / 21 |
|
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
| Services | |
96.34% |
79 / 82 |
|
91.67% |
11 / 12 |
|
50.00% |
1 / 2 |
| Address.php | n/a |
0 / 0 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|||
| ImportLog.php | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
| ImportLogError.php | |
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
| Store.php | |
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
| User.php | n/a |
0 / 0 |
n/a |
0 / 0 |
n/a |
0 / 0 |


Create an Account
First Name
Last Name
Username
Make it Unique!
Email
Don't forget the @ sign!
Password
Make it as long and as crazy as you'd like
Sign Up!
or
Log In


|
masscalculator-core
|
The test fixture for testing class StainlessSteel. More...
#include <stainless_steel_test.h>
Protected Member Functions | |
| StainlessSteelTest () | |
| Construct a new StainlessSteelTest object. | |
| ~StainlessSteelTest ()=default | |
| Destroy the StainlessSteelTest object. | |
Protected Attributes | |
| MassCalculator::Materials::StainlessSteel | material_SS_301 |
| Member objects for class Stainless Steel for each Stainless Steel type. | |
| MassCalculator::Materials::StainlessSteel | material_SS_302 |
| MassCalculator::Materials::StainlessSteel | material_SS_303 |
| MassCalculator::Materials::StainlessSteel | material_SS_304 |
| MassCalculator::Materials::StainlessSteel | material_SS_305 |
| MassCalculator::Materials::StainlessSteel | material_SS_316 |
| MassCalculator::Materials::StainlessSteel | material_SS_321 |
| MassCalculator::Materials::StainlessSteel | material_SS_409 |
| MassCalculator::Materials::StainlessSteel | material_SS_410 |
| MassCalculator::Materials::StainlessSteel | material_SS_420 |
| MassCalculator::Materials::StainlessSteel | material_SS_430 |
| MassCalculator::Materials::StainlessSteel | material_SS_15_5 |
| MassCalculator::Materials::StainlessSteel | material_SS_17_4 |
The test fixture for testing class StainlessSteel.


182. Open the brackets
time limit per test: 0.5 sec.
memory limit per test: 1024
KB
input: standard
output: standard
There is a syntactically correct boolean expression.
The
definition of syntactically correct expression follows as:
1)
"a","b","c",...,"j" are syntactically correct expressions.
2) If A is a
correct expression, then !A and (A) are correct expressions too.
3) If A is
a correct expression and B is a correct expression, then A||B, A&B,
A<=>B, A=>B, A#B are syntactically correct expressions too.
Syntactically correct expression doesn't contain spaces.
Small Latin
letters are variables, ! denotes negation, || - disjunction, & -
conjunction, <=> - equality, => - implication, # - excepting or.
Negation has the highest priority, conjunction has middle priority, and
other operations have low priority. Brackets change the order of operations
executing.
Two expressions are called identical if their values are the same
in any values of variables.
Make the expression, which will be identical
with given expression. New expression must be free of brackets.
Input
Single line of input contains the correct expression; its length
does not exceed 2048 symbols. Variables are denoted by small Latin letters from
'a' to 'j'.
Output
In the first line of output write the sought for expression. Its
length must not exceed 32768 symbols.
Sample test(s)
Input
a&(b||c)
Output
a&b||a&c
| Author: |
Dmitry Orlov |
| Resource: |
Saratov ST team Spring Contest #1 |
| Date: |
18.05.2003
|
| Server time: 2004-01-14 07:33:18 |
Online Contester Team © 2002 - 2003. All
rights
reserved. | | |


Music Library
924 Songs
A-Z
Sort Songs
Shuffle All
Equalizer
Settings
s/n
Title
Time
Artist
Album
Like
More
#
01
Bad blood
03:50
Taylor Swift
Red
03
Perfect
03:50
One Direction
Made in the a.m
02
Dance in the rain
04:00
2face Idibia
2baba
03
Perfect
03:50
One Direction
Made in the a.m
02
Dance in the rain
04:00
2face Idibia
2baba
03
Perfect
03:50
One Direction
Made in the a.m
A
02
Dance in the rain
04:00
2face Idibia
2baba
03
Perfect
03:50
One Direction
Made in the a.m
02
Dance in the rain
04:00
2face Idibia
2baba
03
Perfect
03:50
One Direction
Made in the a.m
02
Dance in the rain
04:00
2face Idibia
2baba
03
Perfect
03:50
One Direction
Made in the a.m
02
Dance in the rain
04:00
2face Idibia
2baba
B


Map Editor
Back
|
Save
|
640x480
800x600
1024x768
1280x800
Export
|
Feedback
|
Need help?
Name:
Description:
Edit
Delete
Name
Participants:
Value
Ubiquity
Loading...


through interactive learning with the Leap Motion Sensor.
About Leap Motion technologyASLeap is an interactive learning program for both the hearing and the deaf. The program teaches users American Sign Language (ASL) using the Leap Motion Controller to recognize the accuracy of each alphabet sign. The Leap Trainer recognizes hand poses and gestures as 3 dimensional coordinates that we then assign to letters of the ASL alphabet. This website serves as a teaching tool featuring games and activities that allow users to learn the ASL Alphabet in fun and interactive ways.


oc
The OpenShift Container Platform CLI.
Allows for application and container management.
More information: home page
- Log in to the OpenShift Container Platform server:
oc login
- Create a new project:
oc new-project project_name
- Switch to an existing project:
oc project project_name
- Add a new application to a project:
oc new-app repo_url --name application
- Open a remote shell session to a container:
oc rsh pod_name
- List pods in a project:
oc get pods
- Log out from the current session:
oc logout


I’m passionate about improving the lives of others through design and am constantly looking to learn new things everyday.
Responsive website redesign to lend a helping paw.
Tell us what’s in your fridge, and we’ll tell you what to cook!


Notebooks
Story: Skinning and cutting
Skinning and cutting
Title
Skinning and cutting
Collection
Summary
Stone–knives. (L VIII.-27. 8434). Reed and stone used for cutting purposes. (L VIII.-26. 8313 and 8314). The power of cutting possessed by a reed and by quartz. (L. VII.-21. 7826 rev. and 7827 rev., note). The people break off a piece of the reed and use it to cut already cooked meat (which is easier to cut).
Comments
1) This story is found in Book VIII-26
Contributors
|han≠kass'o (Klein Jantje) (VIII)
Date
11 March (story given on or about)
Categories
Artefact and dress, Custom and daily life
Keywords
cutting (skinning and) , cutting (methods) , cutting (implements used for) , cutting (of cooked meat) , cutting (using reeds) , reeds (are bitten and divided) , reeds (used for cutting meat) , reeds (methods of cutting and skinning with) , stone (is divided and used for skinning flesh) , stone (methods of cutting and skinning with) , stone (methods of dividing) , stone ('we striking divide') , skinning (and cutting) , skinning ('flaying') , skinning (of springbok) , skinning (of meat) , skinning (of game) , skinning (using a stone) , food (eating springbok flesh) , food (implements used in its preparation) , food (skinning and cutting meat) , implements (used for skinning and cutting meat) , implements (use of reed and stone)
Story Pages
8313-8314


Simone moved to Golden from Colorado Springs in 1997 to attend Colorado School of Mines. She started as a Physics major, but once she took a computer programming course she was hooked. She switched majors and graduated with honors in Math and Computer Science, with a minor in Physics. The year was 2001, just in time for the tech bubble to burst. Like many of today's millennials, she took whatever work she could find to make rent after graduation. Luckily for her, the programming job market had recovered by 2003. Additionally, in 1997 you could attend School of Mines for $2,500 a semester (a THIRD of what it costs today). Dorm, food, book, and student fees are likewise much higher now than inflation can justify. Simone believes our children should have the same opportunities we did.
It took Simone over a decade to get from the IT helpdesk at the National Park Service to writing ground control software for GPS Satellites at Raytheon. She never gave up. Along the way she wrote code in a variety of politically sensitive industries including email security, geological surveying, health insurance claims processing, and defense contracting. Programmers get a great perspective on the industries they work for. To create the systems that everyone uses, they need end-to-end knowledge of workflows at a practical level. They have deep industry knowledge without deep financial ties. Simone has worked both with extremely large systems serving millions of users, and mission critical systems responsible for human safety. She understands how to balance the desire to improve things with respect for the functionality of existing systems. Ideologues tend to forget that you need to understand something before you seek to change it.
Who better to deal with a lobbyist than someone who knows enough about their industries to catch them when they leave out critical context? And who knows better than a programmer that what you wrote doesn't always do what you intended?
Our political discourse has become consumed with hatred and spite. Every question, every issue, gets disconnected from the solvable, real-world, problems in front of us and becomes an ideological battle against some enemy. Radical politicians—on both sides—spend their time playing the blame game. They demonize a section of the population, declare them the author of all our woes, then promise that things will magically be better if we can just properly punish "those people." Who "those people" are varies depending on who you talk to, but what is nearly universal is that the proposals to punish "them" don’t actually address our real problems in any meaningful way. This attitude is a distraction from real solutions - as scapegoating always is. Responsible politicians - on both sides - are then savaged by their respective radicals for the sin of not hating "those people" enough.
Simone Aiken will not play that game. She is a geek, and an engineer. She only wants to fix things.


Callum Smith beats John Ryder on points to retain WBA super-middleweight title
Callum Smith fended off a spirited challenge by John Ryder to win by unanimous decision and retain his WBA super-middleweight title in Liverpool.
Hopes were high in Smith's home city for a big win but he was pushed hard by the Londoner and it seemed after 12 rounds the decision would be close.
However, the three judges scored it 117-111, 116-112 and 116-112 to Smith.
It means Smith, 29, defended the title for a second time after beating Hassan N'Dam in New York in June.
"I was probably sulking because I knew it was not my best performance," Smith, who extended his unbeaten record to 27-0, told BBC Radio 5 live.
"I thought it was a little bit too easy early on and I don't know if it was his game plan to finish strong and it made it awkward.
"I am very disappointed in myself, I am a perfectionist. I knew it was close but I never thought I was behind."
Smith, who beat George Groves to become World Boxing Super Series champion last year, had been the 1-25 on favourite to win before the fight with some bookmakers, such was the level of expectation that he would see out a comfortable win.
But, after a steady start by the champion, southpaw Ryder started to make things awkward for his opponent as he found a way to overcome Smith's height and reach advantage
'I've proven myself on the world scene' - Ryder
"I've gone in against the world number one there and feel like I edged it," said a defiant Ryder.
"I have proven myself on the world scene and deserve to come again. He is a great fighter but I live and breathe this. I re-locate, I am there day in and day out. We have not had the rub of the green but that is boxing.
"I feel that was harsh. I feel I forced the fight a lot and did enough to win."
By the end of the penultimate round, there was little between the two with BBC Radio 5 live co-commentator Steve Bunce having Smith 6-5 up.
Smith started the final round strongly, landing a powerful one-two and then heavy right hand but Ryder fought with all his remaining energy with the two up against ropes at the final bell.
Both Smith and Ryder's corner then celebrated, confident that they had done enough to take victory before the judges' scorecards confirmed Smith's win.
'Smith has gone to places he's never been before'
"They (Smith's team) know tonight they have got away with it," said Bunce.
"I have no problem with Smith winning by a round or two but those scores in my opinion are a little bit wide.
"This guy Ryder, the run he had in his last four fights, he probably wasn't meant to win one of them but he won four. Smith had gone to places he has never been before."


Parent DisplayObject Library display.* Revision Current Public Release (2013.2076) Keywords group, display group, groups See also display.newGroup()
Display Groups and Coordinates
Group objects are a special type of DisplayObject. You can add other display objects as children of a group object. You can also remove them. Even if an object is not visible, it remains in the group object until explicitly removed. Thus, to minimize memory consumption, you should explicitly remove any object that will no longer be used.
All objecs are implicitly added to the current stage, which itself is a kind of group object.
Corona does not have layers or levels but groups can be used to simulate the functionally. Objects added to groups can be moved and transposed as a group by controlling the group object.
Child objects can also be accessed by array index. Example:
group[4]:removeSelf()


Terminology:
Rule of thumb: if loss is significantly lower than val_loss, the data is probably overfitted


Kniffen, Fred. 1960. “The Outdoor Oven in Louisiana.” Louisiana History 1(1): 25–35.
Kniffen, Fred B. 1936. “Louisiana House Types.” Annals of the Association of American Geographers 26(4): 179–93.
Kalčik, S. 1975. “… Like Ann’s Gynecologist or the Time I Was Almost Raped”: Personal Narratives in Women’s Rap Groups. Journal of American Folklore 88(347): 3-11. doi:10.2307/539181


Isaiah 50:1
Thus saith the LORD, Where is the bill of your mother's divorcement, whom I have put away? or which of my creditors is it to whom I have sold you?


A curious combo
by Staff ReporterKathakali Sangeetham and triple jazz — he handles both with aplomb.
Arjun A., Plus One student of New HSS, Nellimoodu, got the first A grade in triple jazz at the Revenue District School Arts Festival on Wednesday.
With triple jazz a competitive event only at the HSS level at the festival, this was Arjun’s first appearance and he made it a memorable one. He also secured the second A grade in Kathakali Sangeetham.
The festival on Thursday will see him take part in the Mridangam event. Parichamuttu and Group Song are the other events he will appear in. Though he has reached the State-level festival in the Mridangam event earlier, it is the drums that are his passion. He has been learning drums for the past three years under Vincent Jose at the Nadabrahma Academy of Fine Arts and Orchestra here.
Arjun has also learnt vocal music, first under Vellayani Ashok Kumar and then under the late Alappuzha Sreekumar, though it was his mother Anitha D. who was his first teacher.
Arjun has tried his hands at violin and guitar too.


The love of Art which each individual uses in defferent ways either by exploring or expressing your feelings.
Logo Design
My Logo Design for my Production, own creation # Exploring my blessing.
Casper
Class Competition in 2014,female inspiration inversly propotionally.
Books
Refletion Of a Man
Find a man with such level of understanding of women.
Robin Sharma
Entreprenuer Spirit and Mind Set.
The Truth About Sex
Code
Mandy's Profile
Implimintation of Self taught on creating my Profile with diffrent Languages,that's were i developed love for programming and studied some shortcuts.
My Application
Languages Used - HTML,CSS,Javascript, IONIC FrameWork and for back-end Microsoft Azure used.
Code space task
Responsive site,I would normally just build a non-responsive site which was a disavantage as a developer wanna be ,well now i know whats suitable for a client.
Sports
Mandy & Nadia
Mach at the Tournament were SMWCC won all the matches.
SMWCC
Promotional league of 2017.
Coaching Workshop
Coaching Workshop that was hosted in New Lands in 2016,The Speaker/Facilitator was Gary Kerstin.
Mandy, Athi & Siya
Womens Tournament that was hosted in Langa Sport Field in 2016,SMWCC Won the Tournament,picture was captured by Phumzile Malotana.
SMWCC (My Team)
The Womens match was hosted in fish hoek 2016.
Cobra's Match
The match was hosted in New Lands on the 8th of Decemember 2017, Cobra's Won the T20.
Ground
Ratanga junction
Awesome Place to clear your mind from everything with Fun & Exploring.
Table Mountain
Hicking with ISPAI Organisation and Naluxolo Students.


FRAMESET
This Tag contains Frame Definitions and Specifies Frame Spacing, Dimensions and Attributes.
This is not to be used in the BODY Tag.
The FRAME and
NOFRAMES Tags are to be used in this Tag.
This is a Container Tag and thus needs to be closed.
Attributes
- COLS :
Specifies the Number and Dimensions of the Vertical Frames
within the Current Frameset. Set COLS to a Comma Separated List of numbers
to indicate the Width of each frame. Use '*' to
Represent a Variable Width .
- ROWS :
Specifies the Number and Dimensions of the Horizontal Frames
within the Current Frameset. Set ROWS to a Comma Separated List of numbers
to indicate the Height of each frame. Use '*' to
Represent a Variable Height .
Syntax
<FRAMESET [COLS="COLS][ROWS="ROWS]>
<FRAME>
</FRAMESET>
Example
<FRAMESET ROWS="40%,60%">
<FRAMESET COLS="40%,60%">
<FRAME NAME="ONE" SCROLLING="AUTO" SRC="frameup.htm">
<FRAME NAME="TWO" SCROLLING="AUTO">
</FRAMESET>
<FRAMESET COLS="50%,50%">
<FRAME NAME="THREE" SCROLLING="YES" NORESIZE SRC="framedown.htm">
<FRAME NAME="FOUR" SCROLLING="NO" NORESIZE>
</FRAMESET>
Effect
Before Looking the Effect, Please refer
to the Frame Tag.
Home
About


Nanyang Technological University

Official Permit
= = = = = = = = = = = = = = = =
Receipt No.:
REDACTED

Student ID:
REDACTED
Name:
REDACTED
Course:
REDACTED
Facility :
Swimming Pool 22
Location :
20 Nanyang Green, Singapore 637715
Date :
01-AUG-2021
Session :
0800 - 0900 hrs
This is a computer generated
permit which requires no signature.
Pls produce this receipt/permit
upon request by staff of S&RCtr
Make use of free locker service in the Centre
Printed Date:
REDACTED


Literate Emacs configuration
Table of Contents
- 1. Early init
- 1.1. Add header for linting
- 1.2. Message the init time and number of garbage collections
- 1.3. Test "best possible" startup time
- 1.4. Make startup faster by reducing the frequency of garbage collection
- 1.5. Require use-package which is used to manage all other packages
- 1.6. Start the Emacs server
- 1.7. Unbind the jdb (Java debugger) function that interferes with my namespacing
- 1.8. Increase usable space and inhibit startup screen
- 1.9. Add footer for linting
- 2. Init
- 2.1. Add header for linting
- 2.2. Set up custom variables group
- 2.3. Set separate custom file
- 2.4. Move save files and avoid interlock files
- 2.5. Clipboards and killing
- 2.6. Configure appearance
- 2.7. Set indentation defaults
- 2.7.1. Set custom variable for single sourcing indentation width
- 2.7.2. Default to spaces
- 2.7.3. Infer whether a file is using spaces or tabs
- 2.7.4. Set default tab display width and tab stop sequence
- 2.7.5. Configure default width for SMIE indentation
- 2.7.6. Set
sh-mode
basic offset - 2.7.7. Set
js-json-mode
andjs-mode
basic offsets - 2.7.8. Stop electric-indent-mode interfering with the previous line
- 2.8. Use envrc environment variables
- 2.9. Use Ivy, Swiper, and Counsel for more effective minibuffers
- 2.10. Use company for better completions
- 2.11. Intercept shell-mode comint commands
- 2.12. xterm-color support
- 2.13. Publish blog
- 2.14. Ligatures
- 2.15. Highlight erroneous whitespace
- 2.16. Add extra information to dired listing
- 2.17. Enable multiple cursors
- 2.18. Expand region at point
- 2.19. Development environments
- 2.20. Forges
- 2.21. Automatically revert buffers if the file changes
- 2.22. Use magit for git
- 2.23. Org
- 2.24. Task management
- 2.25. Use Projectile for working with multiple code projects
- 2.26. HTML
- 2.27. Edit browser text fields within Emacs
- 2.28. Passwords
- 2.29. Email
- 2.30. Jump to a word
- 2.31. Capture a screenshot
- 2.32. Regular expressions for URLs
- 2.33. Conveniently browse to URLs
- 2.34. Manage brightness
- 2.35. Manage sound
- 2.36. Goto for compilation-mode errors
- 2.37. Snippets with yasnippet
- 2.38. Use rx in re-builder
- 2.39. Format everything
- 2.40. NixOS
- 2.41. Git
- 2.42.  Miscellany
- 2.44. Legacy EXWM code
- 2.45. Add footer for linting
Useful reminders for working in this file:
- Add footnotes with
C-c C-x f
1 - Export the file with
C-c C-e
- Add a structure with
C-c C-,
- Tangle org file with
C-c C-v t
- Format buffer with
C-x h C-M-\ C-u C-SPC C-u C-SPC
- Add a source code block tangled into
~/.emacs.d/init.el
with yasnippet keyinit.el
- Add a note with yasnippet key
note
1. Early init
Executed before the startup of the GUI.
1.1. Add header for linting
;;; early-init.el --- Early init file ;;; Commentary: ;;; This file is managed in a literate org file ~/ext/.config/nixos/modules/emacs/config.org ;;; Code:
1.2. Message the init time and number of garbage collections
(add-hook 'emacs-startup-hook (lambda () (message "Emacs ready in %s with %d garbage collections." (format "%.2f seconds" (float-time (time-subtract after-init-time before-init-time))) gcs-done)))
1.3. Test "best possible" startup time2
emacs -q --eval='(message "%s" (emacs-init-time))'
1.4. Make startup faster by reducing the frequency of garbage collection
The default is 800 kilobytes. Measured in bytes.
In the init, this is tuned back down to speed up garbage collections during runtime.
(setq gc-cons-threshold (* 50 1000 1000))
1.5. Require use-package which is used to manage all other packages
(eval-when-compile (require 'package) (add-to-list 'package-archives '("MELPA" . "http://melpa.org/packages/")) (require 'use-package) (setq use-package-expand-minimally byte-compile-current-file))
1.6. Start the Emacs server
(server-start)
1.7. Unbind the jdb (Java debugger) function that interferes with my namespacing
(fmakunbound 'jdb)
1.8. Increase usable space and inhibit startup screen
(setq inhibit-startup-screen t) ;; Disable menu, tool, and scroll bars. (menu-bar-mode 0) (tool-bar-mode 0) (scroll-bar-mode 0)
1.9. Add footer for linting
;;; early-init.el ends here
2. Init
2.1. Add header for linting
;;; init.el --- Init file ;;; Commentary: ;;; This file is managed in a literate org file ~/ext/.config/nixos/modules/emacs/config.org ;;; Code:
2.2. Set up custom variables group
(defgroup jdb nil "Personal group." :group 'emacs :version "27" :prefix "jdb/") (defcustom jdb/slack-status--collection nil "Collection of emoji strings useful in Slack statuses." :group 'jdb :type '(repeat string)) (defcustom jdb/co-authored-by--collection nil "Collection of author strings." :group 'jdb :type '(repeat string))
2.3. Set separate custom file
(setq custom-file "~/.emacs.d/custom.el") (load custom-file)
2.4. Move save files and avoid interlock files
As a single user, interlocks aren't very useful.
(setq backup-directory-alist '(("." . "~/.emacs_saves"))) (setq auto-save-file-name-transforms '((".*" "~/.emacs_saves/" t))) (setq create-lockfiles nil)
2.5. Clipboards and killing
2.5.1.  Clear the kill ring
(defun jdb/clear-kill-ring () "Clear the 'kill-ring'." (interactive) (setq kill-ring nil))
2.6. Configure appearance
(load-theme 'modus-vivendi t) ;; Increase font size. (set-face-attribute 'default nil :height 140) (set-face-attribute 'mode-line nil :height 100) ;; Enable globally set relative line numbers. (global-display-line-numbers-mode) (setq display-line-numbers-type 'relative) ;; Display keypresses. (use-package keycast :ensure t :init (keycast-mode) :config (setq keycast-mode-line-remove-tail-elements nil) :defer t) ;; Highlight the current line globally. (global-hl-line-mode)
2.7. Set indentation defaults
2.7.1. Set custom variable for single sourcing indentation width
(defcustom jdb/indent-width 2 "Set default indent width." :group 'jdb :type 'string)
2.7.2. Default to spaces
(setq-default indent-tabs-mode nil)
2.7.3. Infer whether a file is using spaces or tabs
(defun infer-indentation-style () "Infer whether a file is indented with spaces or tabs." (let ((spaces (how-many "^ " (point-min) (point-max))) (tabs (how-many "^\t" (point-min) (point-max)))) (if (> spaces tabs) (setq indent-tabs-mode nil)) (if (> tabs spaces) (setq indent-tabs-mode t)))) (add-hook 'prog-mode-hook 'infer-indentation-style)
2.7.4. Set default tab display width and tab stop sequence
(setq-default tab-width jdb/indent-width)
2.7.5. Configure default width for SMIE indentation
Most modes that indent using SMIE rules will have their own <MODE>-basic-offset
custom varaible.
(setq-default smie-indent-basic jdb/indent-width)
2.7.6. Set sh-mode
basic offset
(setq-default sh-basic-offset jdb/indent-width)
2.7.7. Set js-json-mode
and js-mode
basic offsets
(setq-default js-indent-level jdb/indent-width)
2.7.8. Stop electric-indent-mode interfering with the previous line
(setq-default electric-indent-mode t)
2.8. Use envrc environment variables
(use-package direnv :ensure t :init (direnv-mode))
2.9. Use Ivy, Swiper, and Counsel for more effective minibuffers
(use-package ivy :ensure t :init (ivy-mode) :commands (ivy-mode) :config (setq ivy-display-style 'fancy) (setq ivy-use-virtual-buffers t) (setq ivy-use-selectable-prompt t) (setq enable-recursive-minibuffers t) (setq search-default-mode #'char-fold-to-regexp)) (use-package swiper :ensure t :after ivy :bind (("C-s" . swiper))) (use-package counsel :init (counsel-mode))
2.10. Use company for better completions
(use-package company :ensure t :init (global-company-mode))
2.11. Intercept shell-mode comint commands
(defun shell-comint-send-input () "Interfere with 'comint-send-input' to facilitate redirect to a buffer." (interactive) (let ((line (buffer-substring (line-beginning-position) (line-end-position)))) (cond ((string-match (rx "(compile " (group (* (not ")"))) ")") line) (let ((compilation-buffer-name-function (lambda (_) line))) (direnv-update-environment) (comint-add-to-input-history line) (compile (match-string 1 line)))) ((string-match (rx (* " ") (group (*? any)) (? " ") ">>>" (? " ") "#<" (group (* (not ">"))) ">") line) (let ((compilation-buffer-name-function (lambda (_) (match-string 2 line)))) (direnv-update-environment) (comint-add-to-input-history line) (compile (match-string 1 line)))) (t (comint-send-input))))) (add-hook 'shell-mode-hook (lambda () (local-set-key (kbd "RET") 'shell-comint-send-input)))
2.12. xterm-color support
(use-package xterm-color :ensure t :config ;; xterm-color for shell mode. (add-hook 'shell-mode-hook (lambda () ;; Disable font-locking in this buffer to improve performance (font-lock-mode -1) ;; Prevent font-locking from being re-enabled in this buffer (make-local-variable 'font-lock-function) (setq font-lock-function (lambda (_) nil)) (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter nil t))) ;; xterm-color for compilation-shell-minor-mode (used by comint). ;; TODO: Find alternative solution as this is not recommended by xterm-color due to font-lock ;; performance degradation. (add-hook 'compilation-shell-minor-mode-hook (lambda () (add-hook 'comint-preoutput-filter-functions 'xterm-color-filter nil t))) ;; xterm-color for compilation-mode. (setq compilation-environment '("TERM=xterm-256color")) (defun jdb/advice-compilation-filter (f proc string) (funcall f proc (xterm-color-filter string))) (advice-add 'compilation-filter :around #'jdb/advice-compilation-filter) ;; xterm-color for ripgrep. ;; From https://github.com/dajva/rg.el/issues/65. (define-advice rg-run (:around (orig-fn &rest args) "no-xterm-color") (let ((compilation-start-hook (remove 'my-compilation-start-hook compilation-start-hook)) compilation-environment) (apply orig-fn args))) (setq comint-output-filter-functions (remove 'ansi-color-process-output comint-output-filter-functions)))
2.13. Publish blog
(defun jdb/org-html-postamble (plist) (format "Last update : %s" (format-time-string "%d %b %Y"))) (setq org-html-postamble 'jdb/org-html-postamble) (setq org-publish-project-alist '(("blog" :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "Blog" :base-directory "~/blog/" :publishing-function org-html-publish-to-html :publishing-directory "~/blog" :section-numbers nil :with-toc t)))
2.14. Ligatures
From https://github.com/mickeynp/ligature.el/wiki#cascadia--fira-code.
(use-package ligature :ensure t :config ;; Enable all Cascadia and Fira Code ligatures in programming modes. (ligature-set-ligatures 'prog-mode '(;; == === ==== => =| =>>=>=|=>==>> ==< =/=//=// =~ ;; =:= =!= ("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!"  (rx (+ (or "=" "!" "\." ":" "~")))) ;; ?? ??? ?: ?= ?. ("?" (rx (or ":" "=" "\." (+ "?")))) ;; %% %%% ("%" (rx (+ "%"))) ;; |> ||> |||> ||||> |] |} || ||| |-> ||-|| ;; |->>-||-<<-| |- |== ||=|| ;; |==>>==<<==<=>==//==/=!==:===> ("|" (rx (+ (or ">" "<" "|" "/" ":" "!" "}" "\]" "-" "=" )))) ;; \\ \\\ \/ ("\\" (rx (or "/" (+ "\\")))) ;; ++ +++ ++++ +> ("+" (rx (or ">" (+ "+")))) ;; :: ::: :::: :> :< := :// ::= (":" (rx (or ">" "<" "=" "//" ":=" (+ ":")))) ;; // /// //// /\ /* /> /===:===!=//===>>==>==/ ("/" (rx (+ (or ">" "<" "|" "/" "\\" "\*" ":" "!" "=")))) ;; .. ... .... .= .- .? ..= ..< ("\." (rx (or "=" "-" "\?"   "(" "\?" "\[" "{" "_(" "_" (+ "#")))) ;; ~~ ~~~ ~= ~- ~@ ~> ~~> ("~" (rx (or ">" "=" "-" "@" "~>" (+ "~")))) ;; __ ___ ____ _|_ __|____|_ ("_" (rx (+ (or "_" "|")))) ;; Fira code: 0xFF 0x12 ("0" (rx (and "x" (+ (in "A-F" "a-f" "0-9"))))) ;; Fira code: "Fl" "Tl" "fi" "fj" "fl" "ft" ;; The few not covered by the regexps. "{|" "[|" "]#" "(*" "}#" "$>" "^=")))
2.15. Highlight erroneous whitespace
(use-package whitespace :ensure t :init (global-whitespace-mode 1) :config (setq-default whitespace-line-column 160) (setq-default whitespace-style '(face trailing tabs lines lines-tail newline indentation space-after-tab empty space-before-tab tab-mark newline-mark)) ;; From: https://emacs.stackexchange.com/questions/38771/magit-status-does-not-open-when-using-global-whitespace-mode-1/38779. (add-function :before-while whitespace-enable-predicate (lambda () (not (derived-mode-p #'magit-mode #'shell-mode)))))
2.16. Add extra information to dired listing
(use-package dired :config (setq dired-listing-switches "-alh"))
2.17. Enable multiple cursors
(use-package multiple-cursors :ensure t :bind (("C->" . mc/mark-next-like-this) ("C-<" . mc/mark-previous-like-this) ("C-c C-<" . mc/mark-all-like-this)))
2.18. Expand region at point
(use-package expand-region :ensure t :bind (("C-." . er/expand-region) ("C-," . er/contract-region)))
2.19. Development environments
2.19.1. Flycheck
(use-package flycheck :config (global-flycheck-mode 1) ;; Set flycheck to inherit the Emacs load path configured by Nix. (setq flycheck-emacs-lisp-load-path 'inherit)) (use-package flycheck-golangci-lint :hook (go-mode . flycheck-golangci-lint-setup) :config (defvar-local flycheck-local-checkers nil) (setq flycheck-golangci-lint-enable-all t) (setq flycheck-golangci-lint-disable-linters '("lll")))
2.19.2. LSP
Useful configuration for Go is documented in https://github.com/golang/tools/blob/master/gopls/doc/emacs.md.
(use-package eglot :after company project yasnippet :config (use-package project) (defun project-find-go-module (dir) (when-let ((root (locate-dominating-file dir "go.mod"))) (cons 'go-module root))) (cl-defmethod project-root ((project (head go-module))) (cdr project)) (add-hook 'project-find-functions #'project-find-go-module) (setq-default eglot-workspace-configuration '((:gopls . ((gofumpt . t))))) ;; The depth of -10 places this before eglot's willSave notification, ;; so that that notification reports the actual contents that will be saved. (defun eglot-format-buffer-on-save () (add-hook 'before-save-hook #'eglot-format-buffer -10 t)) (add-hook 'go-mode-hook #'eglot-format-buffer-on-save) :hook (nix-mode . eglot-ensure) (go-mode . eglot-ensure))
2.19.3. Agda
(use-package agda2-mode)
2.19.4. Bash
(use-package flymake :hook (sh-mode flymake-shellcheck-load))
2.19.5. Emacs Lisp
Display debugger stack frames as a list.
(setq debugger-stack-frame-as-list t)
2.19.6. Go
(use-package go-mode :ensure t :mode "\\.go\\'")
2.19.7. Haskell
(use-package haskell-mode)
2.19.8. JavaScript
(use-package js2-mode :ensure t :mode (("\\.js\\'" . js2-mode)))
(use-package typescript-mode :ensure t :mode "\\.ts\\'" :config (setq typescript-indent-level 2))
2.19.9. Jsonnet
(use-package jsonnet-mode :ensure t :hook (jsonnet-mode . prettify-jsonnet) :config (defun prettify-jsonnet() "Display some jsonnet keywords as pretty Unicode symbols." (setq prettify-symbols-alist '(("function" . ?λ) ("std." . ?) ;; Note this is a zero width space. (": { " . ?.)))) (defun jdb/docs-jsonnet-stdlib () "Open the Jsonnet stdlib documentation." (interactive) (browse-url "https://jsonnet.org/ref/stdlib.html")))
2.19.10. Markdown
- Follow relref targets
(use-package markdown-mode :ensure t :config (defun markdown-relref-translate (filename) "Translate FILENAME into a link that can be followed. Specifically, translating Hugo relrefs into filenames." (replace-regexp-in-string "\\(?:{{< relref \"\\)" "" filename)) (setq markdown-translate-filename-function 'markdown-relref-translate) (defun jdb/markdown-heading-id (heading) "Tranform HEADING into the GitHub identifier that an be used as an anchor." (interactive "sHeading: \n") (message (mapconcat (lambda (char) (cond ((or (char-equal char ?-) (char-equal char ? )) "-") ((memq (get-char-code-property char 'general-category) '(Ll Lu Lo Lt Lm Mn Mc Me Nl)) (char-to-string (downcase char))) (t ""))) heading))))
- Define Vale linters
(use-package flycheck :ensure t :config (flycheck-define-checker vale-error "A checker for prose errors." :command ("~/bin/vale" "--minAlertLevel" "error" source) :standard-input nil :error-patterns ((error line-start (file-name) ":" line ":" column ":" (id (one-or-more (not (any ":")))) ":" (message) line-end)) :modes (markdown-mode org-mode text-mode)) (flycheck-define-checker vale-warning "A checker for prose warnings." :command ("~/bin/vale" "--minAlertLevel" "warning" source) :standard-input nil :error-patterns ((warning line-start (file-name) ":" line ":" column ":" (id (one-or-more (not (any ":")))) ":" (message) line-end)) :modes (markdown-mode org-mode text-mode)) (add-to-list 'flycheck-checkers 'vale-error 'append) (add-to-list 'flycheck-checkers 'vale-warning 'append))
- Define
doc-validator
linter
(use-package flycheck :ensure t :config (flycheck-define-checker doc-validator "A checker Hugo Markdown source."  Nix
(use-package nix-mode :ensure t :init (nix-prettify-global-mode t) :mode "\\.nix\\'")
2.19.12. River
(add-to-list 'load-path "~/ext/jdbaldry/river-mode/") (use-package river-mode :hook (before-save . river-format-before-save))
2.19.13. Terraform
(use-package terraform-mode :ensure t :hook (terraform-mode-hook . terraform-format-on-save-mode))
2.20. Forges
2.20.1. GitHub
- Create a new repository
(use-package ghub :ensure t :commands ghub-post :config (defun jdb/gh-create-repo(repo) "Create a new GitHub repository REPO." (interactive "sRepository: \n") (ghub-post "/user/repos" nil :payload `((name . ,repo)))))
- Define a transient for working with GitHub
- Define a mode for interacting with tabular GitHub data
(defvar jdb/gh-mode-hook nil "Hook executed when `jdb/gh-mode' is run.") (defvar jdb/gh-mode-map (make-sparse-keymap) "Keymap for `jdb/gh-mode' major mode.") (define-derived-mode jdb/gh-mode org-mode "GitHub" "Major mode for working with GitHub tabular output." () (kill-all-local-variables) (use-local-map jdb/gh-mode-map) (setq major-mode 'jdb/gh-mode) (setq mode-name "GitHub") (run-hooks jdb/gh-mode-hook))
- Define a global keybinding for interacting with the transient
(global-set-key (kbd "C-x j") #'jdb/gh)
(use-package transient :config (defun jdb/list-to-shell-string (list) "Convert a possibly nested LIST of strings into a shell command." (string-join (remove nil (flatten-tree list)) " ")) (defun jdb/gh--shell-command (command output-buffer error-buffer) "Run a shell COMMAND with output to OUTPUT-BUFFER, errors to ERROR-BUFFER." (let ((shell-command-dont-erase-buffer 'erase) (resize-mini-windows nil)) (get-buffer-create output-buffer) (get-buffer-create error-buffer) (shell-command (progn (let ((command-buffer (get-buffer-create "*GitHub commands*"))) (with-current-buffer command-buffer (insert (format "$ %s\n" command))) command)) output-buffer error-buffer))) (transient-define-prefix jdb/gh () "Interact with GitHub using the `gh' tool."  ["General commands" [("c" "Create" jdb/gh-issue-create)] [("l" "List" jdb/gh-issue-list)] [("s" "Status" jdb/gh-issue-status)]] ["Targeted commands" [("v" "View" jdb/gh-issue-view)]]) (transient-define-prefix jdb/gh-pr () "Work with GitHub pull requests." ["General commands" ;; [("c" "Create" jdb/gh-issue-create)] [("l" "List" jdb/gh-pr-list)] [("s" "Status" jdb/gh-pr-status)]] ["Targeted commands" [("v" "View" jdb/gh-pr-view)] [("m" "Merge" jdb/gh-pr-merge)]]) (transient-define-infix jdb/gh-issue:--assignee () :argument "--assignee=" :class 'transient-option :description "Filter by assignee" :shortarg "-a") (transient-define-infix jdb/gh-issue:--repo () :argument "--repo=" :class 'transient-option :description "Select a repository using the [HOST/]OWNER/REPO format" :shortarg "-R") (transient-define-infix jdb/gh-issue:--web () :argument "--web" :class 'transient-switch :description "Open in the browser" :shortarg "-w" :init-value (lambda (obj) (oset obj value nil))) (transient-define-infix jdb/gh-issue-create:--body () :argument "--body=" :class 'transient-option :description "Supply a body" :shortarg "-b") (transient-define-infix jdb/gh-issue-create:--labels () :argument "--labels=" :class 'transient-option :description "Add comma-separated labels by name" :shortarg "-l") (defun jdb/gh--read-multiple (prompt initial-input history) (completing-read-multiple prompt nil nil nil nil initial-input history)) (transient-define-infix jdb/gh-issue-create:--milestone () :argument "--milestone=" :class 'transient-option :description "Add the issue to a milestone by name" :shortarg "-m") (transient-define-infix jdb/gh-issue-create:--projects () :argument "--projects=" :class 'transient-option :description "Add the issue to comma-separated list of projects by name" :shortarg "-p") (transient-define-infix jdb/gh-issue-create:--title () :argument "--title=" :class 'transient-option :description "Supply a title" :shortarg "-t") (transient-define-infix jdb/gh-issue-list:--author () :argument "--author=" :class 'transient-option :description "Filter by author" :shortarg "-A") (defun jdb/gh--switch-to-buffer (buffer) "Switch to BUFFER and convert contents to an org table." (switch-to-buffer buffer) (jdb/gh-mode) (org-table-convert-region (point-min) (point-max) 16)) (transient-define-prefix jdb/gh-issue-list () "List GitHub Issues." ["Options" (jdb/gh-issue:--assignee) (jdb/gh-issue:--repo) (jdb/gh-issue-list:--author)] ["Run" [("a" "All" jdb/gh-issue-list:all)] [("l" "List" jdb/gh-issue-list:run)]]) (transient-define-prefix jdb/gh-issue-status () "Get status of GitHub Issues." ["Run" [("s" "Status" jdb/gh-issue-status:run)]]) (defun jdb/gh-issue-status:run () "Show status of relevant issues." (interactive) (let ((output-buffer (get-buffer-create "*GitHub Issues*")) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "issue" ,(transient-args 'jdb/gh-issue) "status" ,(transient-args 'jdb/gh-issue-status))) output-buffer error-buffer) (switch-to-buffer output-buffer))) (transient-define-prefix jdb/gh-issue-create () "Create an issue on GitHub." ["Options" (jdb/gh-issue:--assignee) (jdb/gh-issue:--repo) (jdb/gh-issue:--web) (jdb/gh-issue-create:--body) (jdb/gh-issue-create:--labels) (jdb/gh-issue-create:--milestone) (jdb/gh-issue-create:--projects) (jdb/gh-issue-create:--title)] ["Run" [("c" "Create" jdb/gh-issue-create:run) ("s" "Save" (lambda () (interactive) (message "Saved!") (transient-save)) :transient t)]]) (defun jdb/gh--arg (arg args) "Return the full option of ARG as it appears in ARGS or nil." (let ((value (transient-arg-value arg args))) (cond ((stringp value) (concat arg value)) (value arg)))) (defun jdb/gh--repeat-arg (arg singular args) "Return the comma-separated values for ARG as it appears in ARGS joined by the option SINGULAR, or nil." (let ((values (transient-arg-value arg args))) (if values (string-join (mapcar (lambda (value) (concat singular value)) (string-split values ",")) " ") nil))) (transient-define-suffix jdb/gh-issue-create:run () "Create an issue on GitHub." (interactive) (let* ((output-buffer (get-buffer-create "*GitHub TODO*")) (error-buffer (get-buffer-create "*GitHub errors*")) (args (transient-args 'jdb/gh-issue-create)) (assignee (jdb/gh--arg "--assignee=" args)) (repo (jdb/gh--arg "--repo=" args)) (web (jdb/gh--arg "--web=" args)) (body (jdb/gh--arg "--body=" args)) (labels (jdb/gh--repeat-arg "--labels=" "--label=" args)) (milestone (jdb/gh--arg "--milestone=" args)) (projects (jdb/gh--repeat-arg "--projects=" "--project=" args)) (title (jdb/gh--arg "--title=" args))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "issue" ,repo "create" ,assignee ,body ,labels ,milestone ,projects ,title)) output-buffer error-buffer) (switch-to-buffer output-buffer))) (transient-define-prefix jdb/gh-pr-list () "List GitHub PRs." ["Options" (jdb/gh-issue:--assignee) (jdb/gh-issue-list:--author)] ["Run" [("a" "All" jdb/gh-pr-list:all) ("l" "List" jdb/gh-pr-list:run)]]) (defun jdb/gh-issue-list:all () "List all issues." (interactive) (let ((output-buffer (get-buffer-create "*GitHub Issues*")) (error-buffer (get-buffer-create "*GitHub errors*")) (api-buffer "*GitHub API*")) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "api" "-X" "GET" "search/issues" "-f" "q='assignee:@me is:open type:issue'")) api-buffer error-buffer) (with-current-buffer output-buffer (erase-buffer)) (with-current-buffer api-buffer (goto-char (point-min)) (mapcar (lambda (item) (with-current-buffer output-buffer (insert (format "%s | %s | %s | %s | %s |\n" (string-replace "https://api.github.com/repos/" "" (gethash "repository_url" item)) (gethash "number" item) (gethash "title" item) (gethash "created_at" item) (gethash "updated_at" item) )))) (gethash "items" (json-parse-buffer)))) (jdb/gh--switch-to-buffer output-buffer))) (defun jdb/gh-issue-list:run () "List issues in a repository." (interactive) (let ((output-buffer (get-buffer-create "*GitHub Issues*")) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "issue" ,(transient-args 'jdb/gh-issue) "list" ,(transient-args 'jdb/gh-issue-list))) output-buffer error-buffer) (jdb/gh--switch-to-buffer output-buffer))) (transient-define-prefix jdb/gh-issue-view () "View the GitHub issue near point in a new buffer." ["Options" (jdb/gh-issue:--repo) (jdb/gh-issue:--web)] ["Arguments" (jdb/gh-pr:number)] ["Run" [("v" "View" jdb/gh-issue-view:run)]]) (defun jdb/gh-issue-view:run () "View the GitHub issue near point in a new buffer." (interactive) (let* ((args (message "%s" (key-description (transient-args 'jdb/gh-issue-view)))) (output-buffer (format "*GitHub issue %s*" args)) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "issue" "view" ,args)) output-buffer error-buffer))) (defun jdb/gh-pr-status () "Show status of relevant pull requests." (interactive) (let ((output-buffer (get-buffer-create "*GitHub PRs*")) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "pr" ,(transient-args 'jdb/gh-pr) "status" ,(transient-args 'jdb/gh-pr-status)))) output-buffer error-buffer) (switch-to-buffer output-buffer)) (defun jdb/gh-pr-list:run () "List pull requests in a repository." (interactive) (let ((output-buffer (get-buffer-create "*GitHub PRs*")) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "pr" ,(transient-args 'jdb/gh-pr) "list" ,(transient-args 'jdb/gh-pr-list))) output-buffer error-buffer) (jdb/gh--switch-to-buffer output-buffer))) (defun jdb/gh-pr-list:all () "List all pull requests." (interactive) (let ((output-buffer (get-buffer-create "*GitHub PRs*")) (error-buffer (get-buffer-create "*GitHub errors*")) (api-buffer "*GitHub API*")) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "api" "-X" "GET" "search/issues" "-f" "q='author:@me is:open type:pr'")) api-buffer error-buffer) (with-current-buffer output-buffer (erase-buffer)) (with-current-buffer api-buffer (goto-char (point-min)) (mapcar (lambda (item) (with-current-buffer output-buffer (insert (format "%s | %s | %s | %s | %s |\n" (string-replace "https://api.github.com/repos/" "" (gethash "repository_url" item)) (gethash "number" item) (gethash "title" item) (gethash "created_at" item) (gethash "updated_at" item) )))) (gethash "items" (json-parse-buffer)))) (jdb/gh--switch-to-buffer output-buffer))) (transient-define-infix jdb/gh-pr:number () :argument "number" :shortarg "n" :class 'transient-option :description "Pull request number" :always-read t :allow-empty nil :init-value (lambda (obj) (oset obj value (let ((guess (string-trim (or (caddr (split-string (thing-at-point 'line) "|")) "")))) (if (stringp guess) guess ""))))) (transient-define-prefix jdb/gh-pr-view () "View the GitHub PR near point in a new buffer." ["Options" (jdb/gh-issue:--web)] ["Arguments" (jdb/gh-pr:number)] ["Run" [("v" "View" jdb/gh-pr-view:run)]]) (transient-define-infix jdb/gh-pr-merge:--merge () :argument "--merge" :shortarg "-m" :class 'transient-switch :description "Merge the commits with the base branch" :init-value (lambda (obj) (oset obj value t))) (transient-define-infix jdb/gh-pr-merge:--rebase () :argument "--rebase" :shortarg "-r" :class 'transient-switch :description "Rebase the commits onto the base branch") (transient-define-infix jdb/gh-pr-merge:--squash () :argument "--squash" :shortarg "-s" :class 'transient-switch :description "Squash the commits into one commit and merge it into the base branch") (transient-define-prefix jdb/gh-pr-merge () "Merge the GitHub PR near point in a new buffer." :incompatible '(("--merge" "--rebase" "--squash")) ["Options" (jdb/gh-pr-merge:--merge) (jdb/gh-pr-merge:--rebase) (jdb/gh-pr-merge:--squash)] ["Arguments" (jdb/gh-pr:number)] ["Run" [("m" "Merge" jdb/gh-pr-merge:run)]]) (defun jdb/gh-pr-view:run () "View the GitHub PR near point in a new buffer." (interactive) (let ((output-buffer (get-buffer-create "*GitHub PR*")) (error-buffer (get-buffer-create "*GitHub errors*")) (args (transient-args 'jdb/gh-pr-view))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "pr" ,jdb/gh--repo "view" ,args)) output-buffer error-buffer))) (defun jdb/gh-pr-merge:run () "View the GitHub PR near point in a new buffer." (interactive) (let ((output-buffer (get-buffer-create "*GitHub PR*")) (error-buffer (get-buffer-create "*GitHub errors*"))) (jdb/gh--shell-command (jdb/list-to-shell-string `("gh" "pr" ,(transient-args 'jdb/gh-pr) "merge" ,(transient-args 'jdb/gh-pr-merge))) output-buffer error-buffer))))
- Define a mode for interacting with tabular GitHub data
2.21. Automatically revert buffers if the file changes
(global-auto-revert-mode 1) (add-hook 'dired-mode-hook 'auto-revert-mode)
2.22. Use magit for git
;; TODO: What is looking for a *usually* void variables `n' and `x'? (setq-default n nil) (setq-default x nil) (use-package magit :ensure t :commands (magit-status magit-display-buffer-same-window-except-diff-v1) :config (setq magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1) :defer nil :init (global-set-key (kbd "C-x g") #'magit-status)) (use-package transient :config (setq transient-default-level 7))
2.22.1. Use difftastic for diffing
From https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html.
(use-package transient :config (use-package xterm-color) (defun th/magit--with-difftastic (buffer command) "Run COMMAND with GIT_EXTERNAL_DIFF=difft then show result in BUFFER." (let ((process-environment (cons (concat "GIT_EXTERNAL_DIFF=difft --width=" (number-to-string (frame-width))) process-environment))) ;; Clear the result buffer (we might regenerate a diff, e.g., for ;; the current changes in our working directory). (with-current-buffer buffer (setq buffer-read-only nil) (erase-buffer)) ;; Now spawn a process calling the git COMMAND. (make-process :name (buffer-name buffer) :buffer buffer :command command ;; Don't query for running processes when emacs is quit. :noquery t ;; Show the result buffer once the process has finished. :sentinel (lambda (proc event) (when (eq (process-status proc) 'exit) (with-current-buffer (process-buffer proc) (goto-char (point-min)) (xterm-color-colorize-buffer) (setq buffer-read-only t) (view-mode) (end-of-line) ;; difftastic diffs are usually 2-column side-by-side, ;; so ensure our window is wide enough. (let ((width (current-column))) (while (zerop (forward-line 1)) (end-of-line) (setq width (max (current-column) width))) ;; Add column size of fringes (setq width (+ width (fringe-columns 'left) (fringe-columns 'right))) (goto-char (point-min)) (pop-to-buffer (current-buffer) `(;; If the buffer is that wide that splitting the frame in ;; two side-by-side windows would result in less than ;; 80 columns left, ensure it's shown at the bottom. ,(when (> 80 (- (frame-width) width)) #'display-buffer-at-bottom) (window-width . ,(min width (frame-width)))))))))))) (defun th/magit-show-with-difftastic (rev) "Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft." (interactive (list (or ;; If REV is given, just use it. (when (boundp 'rev) rev) ;; If not invoked with prefix arg, try to guess the REV from ;; point's position. (and (not current-prefix-arg) (or (thing-at-point 'git-revision t) (magit-branch-or-commit-at-point))) ;; Otherwise, query the user. (magit-read-branch-or-commit "Revision")))) (if (not rev) (error "No revision specified") (th/magit--with-difftastic (get-buffer-create (concat "*git show difftastic " rev "*")) (list "git" "--no-pager" "show" "--ext-diff" rev)))) (defun th/magit-diff-with-difftastic (arg) "Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft." (interactive (list (or ;; If RANGE is given, just use it. (when (boundp 'range) range) ;; If prefix arg is given, query the user. (and current-prefix-arg (magit-diff-read-range-or-commit "Range")) ;; Otherwise, auto-guess based on position of point, e.g., based on ;; if we are in the Staged or Unstaged section. (pcase (magit-diff--dwim) ('unmerged (error "unmerged is not yet implemented")) ('unstaged nil) ('staged "--cached") (`(stash . ,value) (error "stash is not yet implemented")) (`(commit . ,value) (format "%s^..%s" value value)) ((and range (pred stringp)) range) (_ (magit-diff-read-range-or-commit "Range/Commit")))))) (let ((name (concat "*git diff difftastic" (if arg (concat " " arg) "") "*"))) (th/magit--with-difftastic (get-buffer-create name) `("git" "--no-pager" "diff" "--ext-diff" ,@(when arg (list arg)))))) (transient-define-prefix jdb/magit-difftastic () "Personal magit commands." ["Difftastic" ("d" "Difftastic Diff (dwim)" th/magit-diff-with-difftastic) ("s" "Difftastic Show" th/magit-show-with-difftastic)]) (define-key magit-status-mode-map (kbd "~") #'jdb/magit-difftastic) (transient-insert-suffix 'magit-dispatch (kbd "h") '("~" "Difftastic" jdb/magit-difftastic)))
2.22.2.  ["Auxiliary commands" ("a" "Add" jdb/add) ("r" "Remove" jdb/rm)]) (define-key magit-status-mode-map (kbd "#") #'jdb/magit-aux) (transient-insert-suffix 'magit-dispatch (kbd "h") '("#" "Aux" jdb/magit-aux)))
2.23. Org
2.23.1. Code execution
- Disable prompt for executing code blocks
(use-package org :config (setq org-confirm-babel-evaluate nil))
- Configure evaluation for Jsonnet
(defun org-babel-execute:jsonnet (body params) "Execute a block of Jsonnet code with org-babel." (let ((input-file (org-babel-temp-file "n" ".jsonnet")) (jsonnet-path (or (cdr (assq :jsonnet-path params)) 0))) (with-temp-file input-file (insert body)) (org-babel-eval (message "JSONNET_PATH=%s jsonnet %s" jsonnet-path (org-babel-process-file-name input-file)) "")))
2.24. Task management
2.24.1. Utilities
Define a regexp for capturing both parts of org-style links.
(defconst org-link-regexp (rx "[[" (group (one-or-more anything)) "][" (group (one-or-more anything)) "]]") "Regexp to match `org-mode' links in the form [[link][text]]. There are capture groups for the link and text components.")
Use the Pomodoro technique
(use-package org-pomodoro :ensure t :hook (org-pomodoro-finished . (lambda () (message "Pomodoro complete!"))))
(use-package org :bind (("C-c l" . org-store-link) ("C-c a" . org-agenda) ("C-c c" . org-capture)) ("C-c C-l u" . jdb/urgent) ("C-c C-l i" . jdb/important) :hook (org-clock-in . jdb/org-slack-status) (org-clock-in . (lambda () (org-todo "PRGR"))) (org-clock-out . (lambda () (jdb/slack-status "" ""))) :config (setq org-startup-folded t) (setq org-adapt-indentation nil) (setq org-todo-keywords '((sequence "TODO" "PRGR" "DONE") (type "NOTD"))) (setq org-todo-keyword-faces '(("PRGR" . "orange") ("NOTD" . "blue"))) (setq org-log-done 'time) (defun jdb/org-30m () "Update effort to 30 minutes." (interactive) (org-set-effort nil "0:30")) (org-defkey org-mode-map (kbd "C-c C-x 3") #'jdb/org-30m) (defun jdb/org-1h () "Update effort to one hour." (interactive) (org-set-effort nil "1:00")) (org-defkey org-mode-map (kbd "C-c C-x 1") #'jdb/org-1h) (defun jdb/org-insert-link-with-title (url) "Insert URL with a description from the title." (interactive "sURL: \n") (org-insert-link nil url (jdb/tag-for-url url 'title)))) (use-package org-clock) (defun jdb/format-YYYY-mm-dd (&optional time) "Format TIME to YYYY-mm-dd. If TIME is not provided, it defaults to the current time." (format-time-string "%Y-%m-%d" time)) (defun jdb/next-working-day () "Return the time of the next working day." (let ((today (string-to-number (format-time-string "%u")))) (if (>= today 5) (+ (time-convert nil 'integer) (* (- 8 today) 86400)) (+ (time-convert nil 'integer) 86400)))) (defun jdb/org-file (&optional time) "Return the org file for the day that TIME falls within. If TIME is not provided it defaults to the current time." (format "~/org/%s.org" (jdb/format-YYYY-mm-dd time))) (defun jdb/org-today () "Create or open the org file for today." (interactive) (find-file (jdb/org-file))) (defun jdb/org-tomorrow () "Create or open the org file for tomorrow." (interactive) (find-file (jdb/org-file (+ (time-convert nil 'integer) 86400)))) (defun jdb/org-next () "Create or open the next org file, only considering work days." (interactive) (find-file (jdb/org-file (jdb/next-working-day)))) (defun jdb/org-file-prev () "Return the org file for the previous working day. This relies on the sorted file names as `yesterday' isn't necessary the last file when files are only created on weekdays." (let ((yesterday (jdb/org-file (- (time-convert nil 'integer) 86400)))) (cond ((file-exists-p yesterday) yesterday) ((file-exists-p (jdb/org-file)) (car (last (butlast (directory-files "~/org" t "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.org$"))))) (t (car (last (directory-files "~/org" t "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.org$"))))))) (defun jdb/org-prev () "Open the org file for the previous working day." (interactive) (find-file (jdb/org-file-prev))) (defun jdb/org-skip () "Skip subtrees with a :personal: tag." (let ((subtree-end (save-excursion (org-end-of-subtree t)))) (if (member "personal" (org-get-tags (point) t)) subtree-end nil))) (defun jdb/add-tag (tag) "Add TAG to current headline." (org-set-tags (sort (cons tag (org-get-tags (point) t)) 'string-lessp))) (defun jdb/org-urgent () "Add urgent tag to headline." (interactive) (jdb/add-tag "urgent")) (defun jdb/org-important () "Add important tag to headline." (interactive) (jdb/add-tag "important")) (defun jdb/org-standup--remaining-effort () "Return the remaining effort in minutes for the org-entry at point." (max (- (org-duration-to-minutes (or (org-entry-get (point) "EFFORT") "0:00")) (org-duration-to-minutes (org-clock-sum-current-item))) 0.0)) (defun jdb/org-standup--org-to-standup () "Translate the `org-todo' entry at point into a standup entry." (format "- %s (EST %s) %s%s" (org-entry-get (point) "TODO") (format-seconds "%02h:%02m" (* 60 (jdb/org-standup--remaining-effort))) (replace-regexp-in-string org-link-regexp "[\\2](\\1)" (org-entry-get (point) "ITEM")) (let* ((tags (split-string (or (car (last (org-heading-components))) "") ":")) (tag-string (concat (if (member "urgent" tags) "U") (if (member "important" tags) "I")))) (if (string-empty-p tag-string) "" (format " [%s]" tag-string))))) (defun jdb/org-standup () "Translate `org-todo' entries into Slack standup message in kill ring." (interactive) (let ((total 0)) (org-map-entries (lambda () (set 'total (+ total (* 60 (jdb/org-standup--remaining-effort))))) t `(,(jdb/org-file)) #'jdb/org-skip) (kill-new (string-join `("*Today*" ,@(org-map-entries 'jdb/org-standup--org-to-standup t `(,(jdb/org-file)) #'jdb/org-skip) ,(format "TOTAL %s" (format-seconds "%02h:%02m" total))) "\n")))) (defun jdb/org-standup-last () "Translate `org-todo' entries into a Slack standup for time spent yesterday." (interactive) (let ((total 0) (yesterday-start (- (string-to-number (shell-command-to-string "date -d '' +%s")) 86400))) (kill-new (string-join `("*Last*" ,@(org-map-entries '(format "- %s (ACT %s ACC %3d٪) %s" (org-entry-get (point) "TODO") (progn (set 'total (+ total (* 60 (or (org-clock-sum-current-item yesterday-start) 0)))) (format-seconds "%02h:%02m" (* 60 (or (org-clock-sum-current-item yesterday-start) 0)))) (let* ((split (split-string (or (org-entry-get (point) "EFFORT") "00:00") ":")) (hours (string-to-number (car split))) (mins (string-to-number (cadr split))) (effort-in-seconds (+ (* 3600 hours) (* 60 mins)))) (* 100 (+ 1 (/ (- (* 60.0 (or (org-clock-sum-current-item yesterday-start) 0)) effort-in-seconds) effort-in-seconds)))) (replace-regexp-in-string org-link-regexp "[\\2](\\1)" (org-entry-get (point) "ITEM"))) t `(,(jdb/org-file-prev)) #'jdb/org-skip) ,(format "TOTAL %s" (format-seconds "%02h:%02m" total))) "\n")))) (defun jdb/org-carryover () "Carry over unfinished tasks from the previous day." (interactive) (let ((curr (jdb/org-file)) (prev (jdb/org-file-prev))) (org-map-entries (lambda () (let ((entry (buffer-substring-no-properties (org-entry-beginning-position) (org-entry-end-position)))) (with-current-buffer (find-file curr) (goto-char (point-max)) (insert entry) (save-buffer)))) nil (list prev) (lambda () (let ((subtree-end (save-excursion (org-end-of-subtree t))) (todo (org-entry-get (point) "TODO"))) (if (or (string= todo "DONE") (string= todo "NOTD")) subtree-end nil)))))) (defun jdb/org-timetable () "Append a time table to the current buffer." (interactive) (with-current-buffer (current-buffer) (goto-char (point-max)) (insert (string-join '("* Total" " #+COLUMNS: %ITEM %TODO %4EFFORT(EST){:} %CLOCKSUM(ACT)" " #+BEGIN: columnview :hlines 1 :id global" " | ITEM | TODO | EST | ACT |" " #+TBLFM:@>$3=vsum(@2..@-1);T::@>$4=vsum(@2..@-1);T" " #+END:") "\n")))) (defun jdb/org-pomodoro-kill () "Cancel a Pomodoro countdown." (interactive) (org-pomodoro-kill))
2.24.2. Interact with Slack
(use-package request) (defvar jdb/slack-api-url "https://slack.com/api") (defun jdb/slack-url-post (endpoint data &optional callback) "Make a POST request to Slack. ENDPOINT is a Slack RPC endpoint such as users.profile.set. DATA is the request body. CALLBACK is called on completion." (request (format "%s/%s" jdb/slack-api-url endpoint) :type "POST" :headers `(("Content-Type" . "application/json; charset=utf-8") ("Authorization" . ,(concat "Bearer " (auth-source-pass-get 'secret "grafana/raintank-corp.slack.com")))) :data data :parser 'json-read :complete (or callback (cl-function (lambda (&key response &allow-other-keys) (message "%s: %s" (request-response-status-code response) (request-response-data response))))))) (defun jdb/slack-standup (text) "Post a standup message TEXT to the standup channel." (interactive "sText: \n") (jdb/slack-url-post "chat.postMessage" (json-encode `((channel . "C039JG5NDLP") (text . ,text))))) (defun jdb/slack-react (channel timestamp name) "React to the TIMESTAMP in CHANNEL with emoji identified by NAME." (jdb/slack-url-post "react.add" (json-encode `((channel . ,channel) (timestamp . ,timestamp) (name . ,name))))) (defun jdb/slack-react-callback (channel timestamp text) "Return a `cl-function' that can be used as a request callback. The callback reacts to the TIMESTAMP message in CHANNEL with the alphabet emoji of the first character in TEXT." (lambda (&key response &allow-other-keys) (if (> (length text) 1) (funcall (jdb/slack-react-callback channel timestamp (substring text 1)) :response response :other-keys) (jdb/slack-react channel timestamp (string-to-char text))))) (defun jdb/slack-post-with-react (text channel &optional message) "Post TEXT to CHANNEL and react with TEXT alphabet emoji. If MESSAGE is non-nil, post that instead of TEXT." (interactive "sText: \nsChannel: \n") (jdb/slack-url-post "chat.postMessage" (json-encode `((channel . ,channel) (text . ,text))) (cl-function (lambda (&key response &allow-other-keys) (let ((channel (alist-get 'channel (request-response-data response))) (timestamp (alist-get 'ts (request-response-data response))) (text (alist-get 'text (alist-get 'message (request-response-data response))))) (funcall (jdb/slack-react-callback channel timestamp text) :response response :other-keys)))))) (defvar jdb/slack-status-last-emoji nil "Last emoji used in a Slack status API request.") (defun jdb/slack-status (text &optional emoji) "Update Slack status. TEXT is the status message. EMOJI is the status emoji." (interactive "sText: \n") (let ((emoji (or emoji (ivy-read "Emoji: " (lambda (&rest _) jdb/slack-status--collection) :action (lambda (emoji) (setq jdb/slack-status-last-emoji emoji)) :caller 'jdb/slack-status)))) (jdb/slack-url-post "users.profile.set" (json-encode `(("profile" . (("status_text" . ,text) ("status_emoji" . ,emoji))))) (cl-function (lambda (&key response &allow-other-keys) (if (and (equal (request-response-status-code response) 200) (equal (alist-get 'ok (request-response-data response)) :json-false) jdb/slack-status-last-emoji) (customize-set-variable 'jdb/slack-status--collection (add-to-list 'jdb/slack-status--collection jdb/slack-status-last-emoji)) (customize-save-customized))))) (jdb/slack-url-post "users.setPresence" (json-encode `(("presence" . ,(if (string-empty-p text) "auto" "away"))))))) (defun jdb/slack-clear () "Clear Slack status." (interactive) (jdb/slack-status "" "")) (defun jdb/slack-status-with-time (text) "Update Slack status with TEXT formatted with the current time. '%s' should be used for the text substition." (interactive "sText: \n") (funcall-interactively 'jdb/slack-status (format text (format-time-string "%H:%M %Z")))) (defun jdb/slack-tea () "Update Slack status to reflect the fact I am making a cup of tea." (interactive) (jdb/slack-status (format "started making tea at %s, back in five minutes" (format-time-string "%H:%M %Z")) ":tea:")) (defun jdb/slack-long-pomodoro () "Update Slack status to reflect that I am on a long Pomodoro break." (interactive) (jdb/slack-status (format "started a long Pomodoro break at %s, back in fifteen minutes" (format-time-string "%H:%M %Z")) ":tomato:")) (defun jdb/slack-lunch () "Update Slack status to reflect the fact I am having lunch." (interactive) (let ((today (string-to-number (format-time-string "%u")))) (jdb/slack-status (format "started lunch at %s, back in one hour" (format-time-string "%H:%M %Z")) (if (>= today 5) ":beer:" ":shallow_pan_of_food:")))) (defun jdb/slack-long-lunch () "Update Slack status to reflect the fact I am having a long lunch." (interactive) (let ((today (string-to-number (format-time-string "%u")))) (jdb/slack-status (format "started a long lunch at %s, back in two hours" (format-time-string "%H:%M %Z")) (if (>= today 5) ":beer:" ":shallow_pan_of_food:")))) (defun jdb/slack-done () "Update Slack status to reflect the fact I am no longer working." (interactive) (jdb/slack-status "not working" ":checkered_flag:")) (defun jdb/conjugate-verb (verb) "Conjugate VERB into present tense. attend -> attending." (cond ((string-suffix-p "e" verb) (replace-regexp-in-string "e$" "ing" verb)) (t (concat verb "ing")))) (defun jdb/org-slack-status () "Update Slack status with the current org item. EMOJI is the status emoji." (let* ((todo (replace-regexp-in-string org-link-regexp "\\2" (org-entry-get (point) "ITEM"))) (words (split-string todo)) (verb (car words)) (conjugated (jdb/conjugate-verb verb)) (text (string-join (cons conjugated (cdr words)) " "))) (funcall-interactively 'jdb/slack-status text)))
2.24.3. Tests
(ert-deftest jdb/org-standup--org-to-standup/should-include-urgency-if-present () "If a TODO entry has an `urgent' tag, it should be included in the standup entry." (let ((org-entry "* TODO must do :urgent:important:") (want "- TODO (EST ) 00:00 must do [UI]")) (with-temp-buffer (insert org-entry) (goto-char (point-min)) (let ((got (jdb/org-standup--org-to-standup))) (print got) (should (equal got want)))))) (ert-deftest jdb/org-standup--remaining-effort/returns-effort-if-no-logbook () "Remaining effort is effort if there is no clocked time." (let ((entry "* TODO todo entry :PROPERTIES: :Effort: 1:00 :END: ")) (with-temp-buffer (insert entry) (goto-char (point-min)) (should (equal (jdb/org-standup--remaining-effort) 60.0))))) (ert-deftest jdb/org-standup--remaining-effort/returns-effort-sub-clock-if-logbook () "Remaining effort should be the defined effort minus any clocked time." (let ((entry "* PRGR todo entry :PROPERTIES: :Effort: 1:00 :END: :LOGBOOK: CLOCK: [1970-01-01 Thu 00:00]--[1970-01-01 Thu 01:00] => 1:00 :END: ")) (with-temp-buffer (insert entry) (goto-char (point-min)) (should (equal (jdb/org-standup--remaining-effort) 0.0))))) (ert-deftest jdb/org-standup--remaining-effort/returns-zero-for-negative-remaining-effort () "Negative effort remaining is not useful for discussing estimated times." (let ((entry "* PRGR todo entry :PROPERTIES: :Effort: 1:00 :END: :LOGBOOK: CLOCK: [1970-01-01 Thu 01:00]--[1970-01-01 Thu 02:00] => 1:00 CLOCK: [1970-01-01 Thu 00:00]--[1970-01-01 Thu 01:00] => 1:00 :END: ")) (with-temp-buffer (insert entry) (goto-char (point-min)) (should (equal (jdb/org-standup--remaining-effort) 0.0))))) (ert-deftest jdb/org-standup--remaining-effort/handles-other-entries-in-buffer () "The original implementation would subtract the clock sum of all entries in the buffer." (let ((entry "* PRGR todo entry :PROPERTIES: :Effort: 1:00 :END: GR other todo entry :PROPERTIES: :Effort: 1:00 :END: :LOGBOOK: CLOCK: [1970-01-01 Thu 01:00]--[1970-01-01 Thu 02:00] => 1:00 CLOCK: [1970-01-01 Thu 00:00]--[1970-01-01 Thu 01:00] => 1:00 :END: ")) (with-temp-buffer (insert entry) (goto-char (point-min)) (should (equal (jdb/org-standup--remaining-effort) 60.0)))))
2.24.4. Interact with Google Calendar
(use-package org-gcal :hook (org-gcal-after-update-entry-functions . my-org-gcal-set-effort) :config (setq org-gcal-recurring-events-mode 'nested) (setq org-gcal-remove-api-cancelled-events t) (setq org-gcal-client-id (auth-source-pass-get "client_id" "grafana/org-gcal")) (setq org-gcal-client-secret (auth-source-pass-get "client_secret" "grafana/org-gcal")) (setq org-gcal-file-alist<EMAIL_ADDRESS>. "~/org/jack.baldry@grafana.com.org"))) (defun my-org-gcal-set-effort (_calendar-id event _update-mode) "Set Effort property based on EVENT if not already set." (when-let* ((stime (plist-get (plist-get event :start) :dateTime)) (etime (plist-get (plist-get event :end) :dateTime)) (diff (float-time (time-subtract (org-gcal--parse-calendar-time-string etime) (org-gcal--parse-calendar-time-string stime)))) (minutes (floor (/ diff 60)))) (let ((effort (org-entry-get (point) org-effort-property))) (unless effort (message "need to set effort - minutes %S" minutes) (org-entry-put (point) org-effort-property (apply #'format "%d:%02d" (cl-floor minutes 60))))))))
2.25. Use Projectile for working with multiple code projects
(use-package projectile :after transient :config (setq projectile-switch-project-action #'(lambda() (magit-status (projectile-acquire-root)))) (setq projectile-completion-system 'ivy) (defun jdb/projectile-project-buffers () (projectile-project-buffers (projectile-acquire-root))) (defun projectile-help () (interactive) ;; From: https://stackoverflow.com/questions/3480173/show-keys-in-emacs-keymap-value (with-output-to-temp-buffer "*keymap: projectile-command-map*" (princ "projectile-command-map\n\n") (princ (substitute-command-keys "\\{projectile-command-map}")))) (transient-define-prefix projectile-search-transient () "Transient for searching a projectile project" ["Search with?" ("r" "Ripgrep" projectile-ripgrep)]) (transient-define-prefix projectile-transient () "Transient for projectile-mode-map" ["Projectile actions" ("c" "Compile" projectile-compile-project) ("e" "Exit" projectile-kill-buffers) ("f" "Find" projectile-find-file) ("p" "Switch project" projectile-switch-project) ("q" "Switch open project" projectile-switch-open-project) ("r" "Replace" projectile-replace) ("s" "Search" projectile-search-transient) ("S" "Save" projectile-save-project-buffers) ("x" "Shell" projectile-run-shell) ("?" "Help" projectile-help)]) :bind ((:map projectile-mode-map ("C-c p" . projectile-transient))) :init (projectile-mode))
2.26. HTML
Functions for fetching and manipulating HTML.
(use-package dom) (defun jdb/tag-for-url (url tag) "Fetch the HTML TAG for a URL. TODO: strip off #edit from at least GDocs URLs as it breaks the request." (interactive "sURL: \nSTag: \n") (let ((buffer (generate-new-buffer "title-for-url-as-kill"))) (with-temp-file "/tmp/gdoc" (let ((effective-url (shell-command-to-string (format "curl -A 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' -Lsb <(kooky -d %s -o /dev/stdout %s) -w %%{url_effective} -o /dev/null %s" (url-host (url-generic-parse-url url)) url url)))) (shell-command (format "curl -Lb <(kooky -d %s -o /dev/stdout) %s" (url-host (url-generic-parse-url effective-url)) effective-url) (current-buffer)) (dom-text (dom-by-tag (libxml-parse-html-region (point-min) (point-max)) tag)))))) (defun jdb/title-for-url-as-kill (url) "Fetch the HTML title for a URL." (interactive "sURL: \n") (kill-new (jdb/tag-for-url url 'title))) (defun jdb/title-for-url-as-kill-md (url) "Fetch the HTML title for a URL." (interactive "sURL: \n") (kill-new (format "[%s](%s)" (jdb/tag-for-url url 'title) url))) (defun jdb/h1-for-url-as-kill (url) "Fetch the first HTML H1 for a URL." (interactive "sURL: \n") (kill-new (jdb/tag-for-url url 'h1))) (defun jdb/h1-for-url-as-kill-md (url) "Fetch the HTML h1 for a URL." (interactive "sURL: \n") (kill-new (format "[%s](%s)" (jdb/tag-for-url url 'h1) url)))
2.27. Edit browser text fields within Emacs
(use-package atomic-chrome :ensure t :commands atomic-chrome-start-server :config (setq atomic-chrome-extension-type-list '(ghost-text)) (atomic-chrome-start-server))
2.28. Passwords
2.28.1. Start Pinentry service to have gpg commands interact with Emacs
(use-package pinentry :ensure t :init (setq epg-pinentry-mode 'loopback) (pinentry-start))
2.28.2. Configure the Unix password manager pass as an auth source
(use-package pass :after pinentry :ensure t :init (auth-source-pass-enable) :config (setq auth-sources '("~/.authinfo" password-store)))
2.29. Email
;;; mue4e (with-eval-after-load 'f (let ((mu4epath (concat (f-dirname (file-truename (executable-find "mu"))) "/../share/emacs/site-lisp/mu4e"))) (when (and (string-prefix-p "/nix/store/" mu4epath) (file-directory-p mu4epath)) (add-to-list 'load-path mu4epath)))) (use-package mu4e :defer t :commands (mu4e mu4e-message mu4e-message-contact-field-matches) :config (setq mu4e-change-filenames-when-moving t) (setq mu4e-contexts `(,(make-mu4e-context :name "Grafana" :enter-func (lambda () (mu4e-message "Entering Grafana context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :leave-func (lambda () (mu4e-message "Leaving Grafana context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :match-func (lambda (msg) (when msg (or (mu4e-message-contact-field-matches msg :to<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :from<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :cc<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :bcc "jack.baldry@grafana.com")))) :vars '((user-full-name . "Jack Baldry") (user-mail-address<EMAIL_ADDRESS>(mu4e-compose-signature . (format "Cheers,\n\njdb")) (mu4e-get-mail-command . "mbsync grafana") (mu4e-maildir-shortcuts . ((:maildir "/grafana/Archive" :key ?a))) (mu4e-bookmarks . ((:name "Unread messages" :query "maildir:/grafana/Inbox AND flag:unread AND NOT flag:trashed" :key ?u) (:name "Archive" :query "maildir:/grafana/Archive" :key ?a) (:name "Last 2 days" :query "maildir:/grafana/Inbox AND date:2d..now AND NOT flag:trashed" :key ?t) (:name "Last 7 days" :query "maildir:/grafana/Inbox AND date:7d..now AND NOT flag:trashed" :key ?w) (:name "This year" :query "maildir:/grafana/Inbox AND date:20220101..now AND NOT flag:trashed" :key ?y) (:name "Deleted" :query "flag:trashed" :key ?d))))) ,(make-mu4e-context :name "gmail" :enter-func (lambda () (mu4e-message "Entering gmail context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :leave-func (lambda () (mu4e-message "Leaving gmail context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :match-func (lambda (msg) (when msg (or (mu4e-message-contact-field-matches msg :to<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :from<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :cc<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :bcc "jdbaldry@gmail.com")))) :vars '((user-full-name . "Jack Baldry") (user-mail-address<EMAIL_ADDRESS>(mu4e-compose-signature . (format "Cheers,\n\njdb")) (mu4e-get-mail-command . "mbsync gmail") (mu4e-maildir-shortcuts . ((:maildir "/gmail/Archive" :key ?a))) (mu4e-bookmarks . ((:name "Unread messages" :query "maildir:/gmail/Inbox AND flag:unread AND NOT flag:trashed" :key ?u) (:name "Archive" :query "maildir:/gmail/Archive" :key ?a) (:name "Last 2 days" :query "maildir:/gmail/Inbox AND date:2d..now AND NOT flag:trashed" :key ?t) (:name "Last 7 days" :query "maildir:/gmail/Inbox AND date:7d..now AND NOT flag:trashed" :key ?w) (:name "Deleted" :query "flag:trashed" :key ?d))))) ,(make-mu4e-context :name "fastmail" :enter-func (lambda () (mu4e-message "Entering fastmail context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :leave-func (lambda () (mu4e-message "Leaving fastmail context") (when (string-match-p (buffer-name (current-buffer)) "mu4e-main") (revert-buffer))) :match-func (lambda (msg) (when msg (or (mu4e-message-contact-field-matches msg :to<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :from<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :cc<EMAIL_ADDRESS>(mu4e-message-contact-field-matches msg :bcc "jdbaldry@fastmail.com")))) :vars '((user-full-name . "Jack Baldry") (user-mail-address<EMAIL_ADDRESS>(mu4e-compose-signature . (format "Cheers,\n\njdb")) (mu4e-get-mail-command . "mbsync fastmail") (mu4e-maildir-shortcuts . ((:maildir "/fastmail/Archive" :key ?a))) (mu4e-bookmarks . ((:name "Unread messages" :query "maildir:/fastmail/Inbox AND flag:unread AND NOT flag:trashed" :key ?u) (:name "Archive" :query "maildir:/fastmail/Archive" :key ?a) (:name "Last 2 days" :query "maildir:/fastmail/Inbox AND date:2d..now AND NOT flag:trashed" :key ?t) (:name "Last 7 days" :query "maildir:/fastmail/Inbox AND date:7d..now AND NOT flag:trashed" :key ?w) (:name "Deleted" :query "flag:trashed" :key ?d))))))) (setq send-mail-function 'sendmail-send-it) (setq sendmail-program (executable-find "msmtp")) (setq mu4e-attachment-dir "~/Downloads") (setq mu4e-get-mail-command "mbsync -a") (setq mu4e-headers-date-format "%F") (setq mu4e-headers-time-format "%H:%M:%S") (setq mu4e-headers-include-related nil) (setq mu4e-html2text-command "iconv -c -t utf-8 | pandoc -f html -t plain") (add-to-list 'mu4e-view-actions '("View in browser" . mu4e-action-view-in-browser) t) (use-package ace-link :defer t :config (add-to-list 'mu4e-view-actions `("Open link" . ,(lambda (_) (ace-link-mu4e))) t)))
2.30. Jump to a word
(use-package avy :ensure t :bind (("M-s" . avy-goto-word-1)))
2.31. Capture a screenshot
(defun jdb/scrot () "Capture a screenshot and store it in the ~/screenshots/ directory." (interactive) (start-process-shell-command "grimshot" nil "grimshot save window ${HOME}/screenshots/$(date '+%Y-%m-%dT%H:%M:%S').png"))
2.32. Regular expressions for URLs
Regular expressions for HTTP URLs constructed to be match all the nonterminals defined in the BNF in RFC1738.
(rx-define alphadigit alphanumeric) (rx-define digits (+ digit)) (rx-define domainlabel (| alphadigit (* alphadigit (? (| alphadigit "-")) alphadigit))) (rx-define toplabel (| alpha (* alpha (? (| alphadigit "-")) alphadigit))) (rx-define hostname (seq (* domainlabel ".") toplabel)) (rx-define hostnumber (seq digits "." digits "." digits "." digits)) (rx-define host (| hostname hostnumber)) (rx-define port digits) (rx-define hostport (seq host (? ":" port))) (rx-define safe (any "$-_.+")) (rx-define extra (any "!*',()")) (rx-define unreserved (| alpha digit safe extra)) (rx-define escape (seq "%" hex hex)) (rx-define uchar (| unreserved escape)) (rx-define hsegment (* (| uchar (any ";:@&=")))) (rx-define hpath (seq hsegment (* "/" hsegment))) (rx-define search (* (| uchar (any ";:@&=")))) (rx-define httpurl (seq "http://" hostport (? "/" hpath (? "?" search)))) (rx-define httpsurl (seq "https://" hostport (? "/" hpath (? "?" search)))) ;; urlish matches HTTPS or HTTP URLs that are just missing a scheme. (rx-define urlish (seq (? (seq "http" (? "s") "://")) hostport (? "/" hpath (? "?" search))))
2.33. Conveniently browse to URLs
(setq-default browse-url-new-window-flag t) (setq browse-url-browser-function 'browse-url-chromium) (defun jdb/chr (incognito url) "Start a chromium process at URL. If INCOGNITO is non-nil, start the chromium incognito." (interactive "P\nsURL: \n") (let ((browse-url-chromium-arguments (if incognito (cons "--incognito" browse-url-chromium-arguments) browse-url-chromium-arguments))) (browse-url-chromium url))) (use-package cl-lib) (defun jdb/chr-open (url buffer-regexp) "Open the buffer matching BUFFER-REGEXP or a new Chromium window for URL." (let ((meet-buffers (cl-remove-if-not (lambda (buffer) (string-match-p buffer-regexp (buffer-name buffer))) (buffer-list)))) (if (> (length meet-buffers) 0) (switch-to-buffer (buffer-name (car meet-buffers)) 'force-same-window) (browse-url url)))) (defun jdb/meet () "Start a Google Meet." (interactive) (jdb/chr-open "https://meet.new" "Meet")) (defun jdb/whatsapp () "Open Whatsapp." (interactive) (jdb/chr-open "https://web.whatsapp.com" "Whatsapp")) (defun jdb/g-calendar () "Open Google Calendar." (interactive) (jdb/chr-open "https://calendar.google.com" "Grafana Labs - Calendar")) (defun jdb/browse-github-notifications () "Open GitHub notifications." (interactive) (jdb/chr-open "https://github.com/notifications" "Notifications")) (defun jdb/browse-github-project () "Open my work GitHub project." (interactive) (jdb/chr-open "https://github.com/orgs/grafana/projects/279/views/1" "jdb")) (defun jdb/yt (query) "Search YouTube with QUERY." (interactive "sQuery: \n") (jdb/chr t (concat "https://youtube.com/results?search_query=" (replace-regexp-in-string " " "+" query)))) (defun jdb/yt-local (url) "Convert a YouTube URL into one for the local player." (interactive "sURL: \n") (string-match (rx (seq "v=" (group (= 11 (any alphanumeric))))) url) (kill-new (format "file:///home/jdb/youtube.html?v=%s&t=0" (match-string-no-properties 1 url)))) (defun jdb/ddg (incognito query) "Search DuckDuckGo for QUERY. IF INCOGNITO is non-nil, search incognito." (interactive "P\nsQuery: \n") (jdb/chr incognito (concat "https://duckduckgo.com/?q=" (replace-regexp-in-string " " "+" query)))) (defun jdb/search-go (package) "Search for PACKAGE on https://pkg.go.dev." (interactive "sPackage: \n") (browse-url (concat "https://pkg.go.dev/search?q=" package))) (defun jdb/search-nixos (package) "Search for PACKAGE in NixOS packages." (interactive "sPackage: \n") (browse-url (concat "https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=" package))) (defun jdb/browse-pulls-on-github (org repo) "Browse my Pull Requests on GitHub for a specific ORG and REPO." (interactive "sOrg: \nsRepository: \n") (browse-url (format "https://github.com/%s/%s/pulls/@me" org repo))) (defun jdb/browse-github (org) "Browse the current file in GitHub. ORG is the Github repository owner." (interactive "sOrg: \n") (let ((url "https://github.com") (repo (car (last (delete "" (split-string (projectile-project-root) "/"))))) (ref (shell-command-to-string "git rev-parse HEAD")) (file (string-remove-prefix (projectile-project-root) (buffer-file-name))) (line (line-number-at-pos))) (browse-url (format "%s/%s/%s/tree/%s/%s#L%s" url org repo ref file line)))) (defun jdb/browse-aoc (year day) "Browse the Advent of Code puzzle corresponding to YEAR and DAY." (interactive "sYear: \nsDay: \n") (browse-url (format "https://adventofcode.com/%s/day/%s" year day))) (defun jdb/browse-geeksforgeeks () "Browse the geeksforgeeks.org problems page." (interactive) (browse-url "https://practice.geeksforgeeks.org/explore?page=1&status[]=unsolved&sortBy=difficulty&utm_source=auth&utm_medium=profile&utm_campaign=empty-data")) (defun jdb/browse-pr (org repo pr) "Browse the GitHub pr number ISSUE for ORG/REPO." (interactive "sOrg: \nsRepo: \nnIssue: \n") (browse-url (format "https://github.com/%s/%s/pr/%s" org repo pr))) (defun jdb/browse-zendesk(id) "Browse a Zendesk ticket. ID is the Zendesk ticket number." (interactive "sID: \n") (let ((url "https://grafana.zendesk.com/agent/tickets")) (browse-url (format "%s/%s" url id)))) (defun jdb/docs-home-manager () "Open the home-manager documentation." (interactive) (browse-url "https://nix-community.github.io/home-manager/")) (defun jdb/soccercatch () "Browse to soccercatch.com." (interactive) (jdb/chr t "https://soccercatch.com"))
2.34. Manage brightness
(use-package f :ensure t :config (defun jdb/brightness (percentage) "Adjust the brightness to PERCENTAGE." (interactive "p") (let* ((backlight-path "/sys/class/backlight/intel_backlight") (path-join (lambda (&rest paths) (string-join paths "/"))) (max-brightness-file (funcall path-join backlight-path "max_brightness")) (max-brightness (string-to-number (f-read-text max-brightness-file))) (brightness-file (funcall path-join backlight-path "brightness")) (brightness (* percentage (/ max-brightness 100)))) (format "tee %s <<<%s" brightness-file brightness))))) (start-process-shell-command "brightness" nil
2.35. Manage sound
(defun jdb/amixer-message () "Output the current volume as a message." (message "%s" (shell-command-to-string "pactl get-sink-volume @DEFAULT_SINK@"))) (defun jdb/amixer-set-vol (vol) "Set the volume to VOL percent." (shell-command (format "pactl set-sink-volume @DEFAULT_SINK@ %s%%" vol))) (defun jdb/amixer-mute () "Mute sound." (interactive) (jdb/amixer-set-vol 0) (jdb/amixer-message)) (defun jdb/amixer-unmute (vol) "Set sound level to 100% if VOL is nil otherwise, set to the value of VOL." (interactive "P") (jdb/amixer-set-vol (or vol 100)) (jdb/amixer-message))
2.36. Goto for compilation-mode errors
(use-package compile :config (add-to-list 'compilation-error-regexp-alist 'markdownlint-cli) (add-to-list 'compilation-error-regexp-alist-alist '(markdownlint-cli . ("^\\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\) .*$" 1 2 3))) (add-to-list 'compilation-error-regexp-alist 'monkeyc) (add-to-list 'compilation-error-regexp-alist-alist '(monkeyc . ("^ERROR: [^:]+: \\([^:]+\\):\\([0-9]+\\),\\([0-9]+\\): .*$" 1 2 3))) (add-to-list 'compilation-error-regexp-alist 'doc-validator) (add-to-list 'compilation-error-regexp-alist-alist '(doc-validator . ("^ERROR: \\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\) .*$" 1 2 3))))
2.37. Snippets with yasnippet
(use-package yasnippet :ensure t :config (yas-global-mode))
2.38. Use rx in re-builder
From https://www.masteringemacs.org/article/re-builder-interactive-regexp-builder.
(use-package re-builder :config (setq reb-re-syntax 'rx))
2.39. Format everything
(use-package format-all :ensure t :hook (prog-mode . format-all-mode) (markdown-mode . format-all-mode) (format-all-mode . format-all-ensure-formatter))
2.40. NixOS
(defun jdb/nixos-flake-update (directory) "Update the system configuration flake. DIRECTORY configures which directory to update the flake in." (interactive "sDirectory: \n") (let ((compilation-buffer-name-function (lambda (_) "*jdb/nixos-flake-update*"))) (compile (format "cd %s && nix flake update" (if (string-blank-p directory) "~/.config/nixos" directory))))) (defun jdb/nixos-rebuild () "Rebuild and switch to the new generation." (interactive) (let ((compilation-buffer-name-function (lambda (_) "*jdb/nixos-rebuild*"))) ;; nix flake lock --update-input input (compile "cd ~/.config/nixos && nix flake lock --update-input xinput_exporter && sudo nixos-rebuild switch --flake ~/.config/nixos" t))) (defun jdb/nix-collect-garbage () "Collect Nix garbage." (interactive) (let ((compilation-buffer-name-function (lambda (_) "*jdb/nix-collect-garbage*"))) (compile "sudo nix-collect-garbage" t)))
2.41. Git
(defun jdb/co-authored-by--grep-authors (regexp) "Find all authors in the git log that match REGEXP." (interactive "sRegexp: \n") (with-current-buffer (generate-new-buffer "grep-authors") (switch-to-buffer (current-buffer)) (start-process-shell-command "grep-authors" (current-buffer) (format "git log | grep %s | sort -u" regexp)))) (defun jdb/co-authored-by () "Add a Co-authored-by line to a commit message." (interactive) (let ((tag "Co-authored-by: ")) (ivy-read tag (lambda (&rest _) jdb/co-authored-by--collection) :action (lambda (author) (customize-set-variable 'jdb/co-authored-by--collection (add-to-list 'jdb/co-authored-by--collection author)) (customize-save-customized) (insert (concat tag author))) :caller 'co-authored-by)))
2.42. Speed up garbage collection
Make gc pauses faster by decreasing the threshold.
(setq gc-cons-threshold (* 2 1000 1000))
2.43. Miscellany
2.43.1. Convert a string to Slack alphabet emojis.
;; (message (jdb/string-to-alphabet-emoji "test:smile:test" nil)) (defun jdb/string-to-alphabet-emoji (str &optional white?) "Display the message STR as Slack alphabet emoji. WHITE? represents whether the character should be yellow (nil) or white (integer value)." (interactive "sMessage: \nP") ;; Taken from: https://emacs.stackexchange.com/questions/7148/get-all-regexp-matches-in-buffer-as-a-list (let ((find-matches (lambda (regexp str) "Return a list of all matches of REGEXP in STR." (let ((pos 0) matches) (while (string-match regexp str pos) (push (match-string 0 str) matches) (setq pos (match-end 0))) (reverse matches))))) (kill-new (message (mapconcat (lambda (token) (let ((color (if white? "yellow" "white")) (emoji? (< 1 (length token)))) (cond (emoji? token) ((and (org-string<= "A" token) (org-string<= token "z")) (format ":alphabet-%s-%s:" color (downcase token))) ((string-equal "!" token) (format ":alphabet-%s-exclamation:" color)) ((string-equal "?" token) (format ":alphabet-%s-question:" color)) ((string-equal "@" token) (format ":alphabet-%s-at:" color)) ((string-equal "#" token) (format ":alphabet-%s-hash:" color)) ((string-equal " " token) " ") (t token)))) (funcall find-matches "\\(:[a-z-_+-]+:\\|.\\)" str) "")))))
2.43.2. Apply an RCS formatted patch to a buffer
;; Taken from https://github.com/dominikh/go-mode.el/blob/08aa90d52f0e7d2ad02f961b554e13329672d7cb/go-mode.el#L1852-L1894 ;; Adjusted to avoid relying on cl-libs or other go-mode internal functions. (defun jdb/apply-rcs-patch (patch-buffer) "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." (let ((target-buffer (current-buffer)) ;; Relative offset between buffer line numbers and line numbers ;; in patch. ;; ;; Line numbers in the patch are based on the source file, so ;; we have to keep an offset when making changes to the ;; buffer. ;; ;; Appending lines decrements the offset (possibly making it ;; negative), deleting lines increments it. This order ;; simplifies the forward-line invocations. (line-offset 0) (column (current-column))) (save-excursion (with-current-buffer patch-buffer (goto-char (point-min)) (while (not (eobp)) (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") (error "Invalid rcs patch or internal error in jdb/apply-rcs-patch")) (forward-line) (let ((action (match-string 1)) (from (string-to-number (match-string 2))) (len (string-to-number (match-string 3)))) (cond ((equal action "a") (let ((start (point))) (forward-line len) (let ((text (buffer-substring start (point)))) (with-current-buffer target-buffer (setq line-offset (- line-offset len)) (goto-char (point-min)) (forward-line (- from len line-offset)) (insert text))))) ((equal action "d") (with-current-buffer target-buffer (goto-char (point-min)) (forward-line (1- (- from line-offset))) (setq line-offset (+ line-offset len)) (dotimes (_ len) (delete-region (point) (save-excursion (move-end-of-line 1) (point))) (delete-char 1)))) (t (error "Invalid rcs patch or internal error in jdb/apply-rcs-patch"))))))) (move-to-column column)))
2.43.3. Implement watch buffer
(defun jdb/watch (command n) "Run a specific shell COMMAND every N seconds."  Caps lock minor mode
(defvar caps-lock-mode-map (let ((map (make-sparse-keymap))) (mapc (lambda (char) (keymap-set map (char-to-string char) 'caps-lock-mode-self-insert-command)) "abcdefghijklmnopqrstuvwxyz") map)) (defun caps-lock-mode-self-insert-command (&optional n) "Like `self-insert-command', but upcase the typed character. The numeric prefix argument N says how many times to repeat the insertion." (interactive "p") (insert-char (upcase last-command-event) n)) (define-minor-mode caps-lock-mode "Insert all latin alphabet characters as caps. It can be fun to disable this mode. Don't forget about `\\[downcase-word]'!" :global t :lighter " CAPS-LOCK")
2.43.5. Zap up to char
(use-package misc :bind (("M-Z" . zap-up-to-char)))
2.43.6. Transpose lines
From https://emacsredux.com/blog/2013/04/02/move-current-line-up-or-down/.
(defun jdb/move-line-up () "Move up the current line." (interactive) (transpose-lines 1) (forward-line -2) (indent-according-to-mode)) (defun jdb/move-line-down () "Move down the current line." (interactive) (forward-line 1) (transpose-lines 1) (forward-line -1) (indent-according-to-mode)) (global-set-key (kbd "M-n") #'jdb/move-line-down) (global-set-key (kbd "M-p") #'jdb/move-line-up)
2.44. Legacy EXWM code
This code was useful when running with EXWM. Perhaps it will be useful again.
;; Configure a logout function. (use-package recentf :commands (jdb/exwm-logout recentf-save-list) :config (defun jdb/quit () "Quit Emacs." (interactive) (recentf-save-list) (save-some-buffers) (save-buffers-kill-emacs))) (defun jdb/chr--switch-buffer-action (buffer) "Switch to a buffer named BUFFER if it is live. If there is nota live buffer, and BUFFER looks an HTTP URL, open it in a browser. Otherwise, search for BUFFER with DuckDuckGo." (cond ((buffer-live-p (get-buffer buffer)) (switch-to-buffer buffer 'force-same-window)) ;; Open URL from history. ((string-match (rx string-start https-urlish " | ") buffer) (jdb/chr current-prefix-arg (nth 0 (split-string buffer)))) ((string-match (rx string-start https-urlish string-end) buffer) (jdb/chr current-prefix-arg buffer)) (t (jdb/ddg current-prefix-arg (string-trim buffer))))) (defun jdb/chr--read-history () "Read Chromium history file and add the URLs to ivy completion." (let* ((wd "/home/jdb/.config/chromium/Default") (history-file (string-join (list wd "History") "/")) (copy-file (concat history-file ".copy"))) ;; Copy the file because Chromium keeps the database locked when running. (copy-file history-file copy-file "overwrite-if-exists") (butlast (split-string (shell-command-to-string (format "sqlite3 %s -separator ' | ' 'SELECT url,title FROM urls'" copy-file)) "\n" nil)))) (use-package s) (defun jdb/chr-read () "Switch to a chromium process or start a new one. INCOGNITO controls whether the window is opened incognito. URL is the optional URL to open the process on." (interactive) (ivy-read "Switch to buffer: " #'(lambda (_ _ _) (append (internal-complete-buffer "Chromium-browser" nil t) (jdb/chr--read-history))) :action #'jdb/chr--switch-buffer-action :caller 'jdb/chr-read)) (global-set-key (kbd "C-x c") 'jdb/chr-read) ;; C-x C-c is originally bound to save-buffers-kill-terminal which is a little too ;; dangerous to have as a typo for jdb/chr-read. (global-unset-key (kbd "C-x C-c"))
2.45. Add footer for linting
(provide 'emacs) ;;; init.el ends here


Get a free balloon pet for every $4 PlushCoin purchase, or all 30 balloon pets available for a $100 Plush Coin purchase in the Goodies Market!
Get a free balloon pet for every $4 PlushCoin purchase, or all 30 balloon pets available for a $100 Plush Coin purchase in the Goodies Market!


Run Oracle applications on DB2 9.7
DB2 9.7 comes with many powerful features that help ease the day-to-day activities all DBAs.
must perform in every IT shop. The major feature in DB2 9.7 is the out-of-the-box support for
Oracle's SQL and PL/SQL dialects. This article will give you a high-level overview of what Oracle
compatibility means in DB2.


1 override of WriteObject
Microsoft.PowerShell.Commands.Management (1)
cimSupport\cmdletization\cim\EnumerateAssociatedInstancesJob.cs (1)
87
internal override void
WriteObject
(object outputObject)
3 references to WriteObject
Microsoft.PowerShell.Commands.Management (3)
cimSupport\cmdletization\cim\EnumerateAssociatedInstancesJob.cs (1)
95
base.
WriteObject
(outputObject);
cimSupport\cmdletization\cim\ExtrinsicMethodInvocationJob.cs (1)
186
this.
WriteObject
(cmdletOutput);
cimSupport\cmdletization\cim\MethodInvocationJobBase.cs (1)
174
this.
WriteObject
(passThruObject);


Have you ever started into a day that you know is going to be bad?Of course you have. We all have. And you’ve even sometimes known it was going to be bad without knowing fully just why...
Continue reading...Sometimes, life changes. Overnight or in a split second. Nothing is ever, ever the same again. You begin to date your life “BC” and “AD.” Before the life-changing event and after it. Life has splintered. Shattered...
Continue reading...


Enter three triangle side lengths:
Your triangle is:
Equilateral!
Isosceles!
Scalene!
Not a triangle!!!