Metadata
-
2013年02月01日
Customizing auctex for line-errors and shell-escape
As it took me quite a while to find this online, I record it here for posterity.
The purpose of the following is to customize the latex command that is run when you’re using auctex for emacs. I’ve seen two cases where I had to do this.
- Sometimes, when I compile some latex, there is an error but when I want to jump to it I get to an empty buffer. The problem is in the parsing of the log (typically an unbalanced parenthesis), so with only the line number auctex does not know where to jump to. To solve this, one adds the
--file-line-errorflag when running latex, and every error will have both the file name and the line number. - When I want to use minted, a great syntax colorizing tool for latex, I need to put the
--shell-escapeflag so that the external pygments can be called.
To add such arguments to latex, one can customize the
TeX-engine-alistvariable. Here is what I did.(setq TeX-engine-alist '( (xetex "XeTeX" "xetex --file-line-error" "xelatex --file-line-error" "xetex") (xetex_sh "XeTeX shell escape" "xetex --file-line-error --shell-escape" "xelatex --file-line-error --shell-escape" "xetex")))
Here is the documentation for this association list.
Each entry is a list with a maximum of five elements. The first element is a symbol used to identify the engine. The second is a string describing the engine. The third is the command to be used for plain TeX. The fourth is the command to be used for LaTeX. The fifth is the command to be used for the –engine parameter of ConTeXt’s texexec program. Each command can either be a variable or a string. An empty string or nil means there is no command available.
I then add some local variables at the end of my tex file to choose which engine to use.
%%% Local Variables: %%% coding: utf-8 %%% mode: latex %%% TeX-engine: xetex_sh %%% TeX-master: t %%% End:
- Sometimes, when I compile some latex, there is an error but when I want to jump to it I get to an empty buffer. The problem is in the parsing of the log (typically an unbalanced parenthesis), so with only the line number auctex does not know where to jump to. To solve this, one adds the
-
2012年11月11日
SSH Tunnel and Screen Sharing through Keyboard Maestro
I just read this great post by Dr. Drang on several ways to use SSH tunnels, and I thought I could offer a small refinement for the Screen Sharing part.
As I often need to view the screen of my
minimachine, I wanted to make it as painless as possible. To this end, I wanted by a single shortcut to open the tunnel and launch the Screen Sharing application (fairly easy to do), and to kill the tunnel when I quit the Screen Sharing application. Here is what I came up with using Keyboard Maestro.Keyboard Maestro Macro
The trick consists in recording the pid of the tunnel when I start it (
echo $!does it, which is then put in a variable), then regularly check if the Screen Sharing application is still running. Finally, when it has stopped, kill the tunnel. This of course could be adapted to simply be a script, but I find that Keyboard Maestro really shines for these simple cases. -
2012年08月09日
Graphing RRD data using gnuplot
I recently replaced my home server which, among other things, monitors the data from my Current Cost installation and creates pretty graphs. To this end, I followed this nicely explained approach, which uses RRDtool both as a data storage and a graphing tool.
Unfortunately, after installing the server, I could not get the
graphcommand of my (homebrew installed) rrdtool to work, as described in this github issue. I haven’t been able to solve this issue, but I’ve found a workaround graphing the data using gnuplot.Getting the data
The first step is to extract the data from the RRD database. The
rrdtool fetchcommand works as expected, but one should get the start and end date be multiple of the desired resolution, as explained in the documentation. I wrote a small script,cc_text.shthat takes two arguments: the resolution (in seconds) and the start time (in time offset specification, see the documentation), and dumps the data on the standard output, assuming the end time is the current time modulo the resolution.#!/bin/sh TIME=$(date +%s) RRDRES=1ドル RREND=2ドル /usr/local/bin/rrdtool fetch /Users/schmitta/Documents/current_cost/powertemp.rrd \ AVERAGE \ -r $RRDRES \ -e $(($TIME/$RRDRES*$RRDRES)) \ -s e-2ドル
The data output looks like this.
Power Temperature 1344521880: 9,9564808206e+01 2,4900000000e+01 1344522060: 1,0056171519e+02 2,4900000000e+01 1344522240: nan nan
To transform it into data that gnuplot will accept, I need to get rid of the lines with
nanin it, and transform the commas in periods. So to extract the data for the last day at a 3 minutes resolution, I do the following in another script (which will see below)./Users/schmitta/bin/cc_text.sh 180 1d | grep -v nan | sed -e 's/,/./g' > $DATA
Graphing the data
When the data is in the correct format, I then call gnuplot to graph it. I have another script that does it,
cc_watt_temp.sh, taking three arguments: the name of the data file, the name of the output png to be generated, and the format for the time on the ‘x’ axis (which is different in yearly and hourly graphs).#!/bin/sh name=1ドル out=2ドル format=3ドル /usr/local/bin/gnuplot <<EOF set terminal png size 1400, 400 set output "$out" set xdata time set timefmt "%s" t0=7200 # Switch for time zone # set xtics 3600 set format x "3ドル" set ytics nomirror set y2tics nomirror # set style fill solid border -1 plot "$name" u (\1ドル+t0):2 axis x1y1 title 'Power' w histeps, "$name" u (\1ドル+t0):(\3ドル-2) axis x1y2 title 'Temp' w lines EOF
The two peculiarities of the script are that I need to take my timezone into account (adding
t0to every date), and that the current cost temperature sensor is two degrees too high (substracting2to every temperature).Gluing it together
I then created a final set of scripts which are regularly called to create the graphs. I have five of them: hourly, daily, weekly, monthly, and yearly graphs. Here is the daily one.
#!/bin/sh DATA=/Users/schmitta/tmp/data1d.data PNG=/Users/schmitta/Dropbox/current_cost/1d.png /Users/schmitta/bin/cc_text.sh 180 1d | grep -v nan | sed -e 's/,/./g' > $DATA /Users/schmitta/bin/cc_watt_temp.sh $DATA $PNG "%H:%M"
As you can see, the graph is created in my DropBox account. I have a local web page that collects all these graphs, which I can access from any computer that is connected to DropBox without having to launch Apache on the server.
All you now need to do is regularly launch these scripts, using for instance cron or launchd.
-
2012年08月02日
Installing and configuring BBDB 3
BBDB 3 is still under development, and as such there is not much information available to simply configure it. The goal of this post is to explain how to obtain a minimal installation of BBDB 3 that works with gnus. The installation part is targeted for OS X, but the configuration is platform independent.
Installation
I recommend getting the development version, as reported bugs can be fixed fairly quickly.
git clone git://git.savannah.nongnu.org/bbdb.git
Next, you will need a recent autoconf. The one provided by Lion is not recent enough, so I had to install it through homebrew. I haven’t installed Mountain Lion yet, so I don’t know which version of autoconf ships with it. Try skipping this step (directly running
autoconf), and if it complains then do the following.brew install autoconf export PATH=/usr/local/Cellar/autoconf/2.69/bin:$PATH
You can then run autoconf.
autoconf
You now can run the configuration script, specifying where the files will be installed. Here is the command I ran. (You should use absolute paths in this command.)
./configure \ --with-lispdir=/Users/schmitta/.emacs.d/vendor/emacs/site-lisp/bbdb \ --with-texmf-dir=/Users/schmitta/Library/texmf \ --infodir=/Users/schmitta/.emacs.d/vendor/info
Compilation and installation can then proceed as usual.
make make install
Configuration
Here is the configuration I put in my
.emacs. I have to thank the very kind people on the org-mode and bbdb mailing lists, who really helped me put this together.If some things are not clear, I highly recommend looking up the variable definitions using for instance
C-h v bbdb-mua-update-interactive-p, which will describe the different options for this setting.;; file where things will be saved (setq bbdb-file "~/.emacs.d/bbdb") ;; load bbdb (add-to-list 'load-path (concat emacsd-vendor-dir "/bbdb")) (require 'bbdb-loaddefs) (require 'bbdb) ;; initialization (bbdb-initialize 'gnus 'message) (bbdb-mua-auto-update-init 'gnus 'message) ;; size of the bbdb popup (setq bbdb-pop-up-window-size 10) ;; What do we do when invoking bbdb interactively (setq bbdb-mua-update-interactive-p '(query . create)) ;; Make sure we look at every address in a message and not only the ;; first one (setq bbdb-message-all-addresses t) ;; use ; on a message to invoke bbdb interactively (add-hook 'gnus-summary-mode-hook (lambda () (define-key gnus-summary-mode-map (kbd ";") 'bbdb-mua-edit-field) )) ;; ;; This is for non interactive bbdb invocation, but it was a bit ;; ;; too much ;; (setq bbdb-update-records-p 'query)
To test this, launch gnus, and when reading a message hit the ’;’ key. You should see a window that offers to create records for the senders and recipients of the message. When writing a message, you can hit ‘TAB’ to complete the email address.
Have fun!
-
2011年12月23日
Window Manipulation with Keyboard Maestro
Since last October, my main computer has been a 13” MacBook Air. It’s a great machine, perfect for what I do (write papers, read papers, code in Ocaml and Coq). Its screen is not that big, so at work I have an Apple Cinema Display hooked to it. I don’t have this luxury at home, thus many times a week I switch from a big display to the smaller integrated one, which means that I have to move windows around. This post is about how I use Keyboard Maestro to help me in one particular case.
For my day to day window moving and resizing needs, I rely on Mercury Mover. I know there are many other applications that let you easily move windows using only the keyboard, but I’ve been using this one for years and it perfectly fits my needs. Unfortunately, moving windows sometimes is not enough. When I read a PDF on the MacBook Air, using Skim of course, I need the PDF to be displayed as “Single Page Continuous”, which along with “Automatically Resize” make it legible to my aging eyes. I set up Skim to use this and a reasonable window size by default. However, when I switch to the big screen, I prefer the PDF to be displayed as “Single Page”. This way, hitting the down arrow key then gets me to the next page. Mercury Mover can’t switch this setting for me, but Keyboard Maestro can.
Keyboard Maestro: PDF Large Screen
By the way, the interface to find the menu item to use is just great.
Keyboard Maestro: Menu Selection
Simple, repetivite task: that’s what Keyboard Maetro is for.
-
2011年11月28日
Sun, Earth, and Moon in Codea
I spent a few hours last week-end coding a very simple simulation of the gravitational interaction of the Sun, Earth, and Moon system. Even if it’s very basic, I was so proud of it I posted a link to it on Twitter, which lead to the great honor or having Dr Drang himself ask for more details. Here they are, along with the code. (You can find the whole code as a gist, if you want to play with it. The code below has additional line breaks to make it look not to bad.)
Screenshot of the simulation
The simulation is written with Codea, a programming environment on the iPad. Codea is currently named Codify but this will change when Apple approves the latest version (soon, hopefully).
The programming language Codea uses is Lua, with a few extensions: a class system, a drawing loop very similar to the one of Processing, and ways to interact with the iPad (touches, accelerometer, and so on). I did not know Lua before playing with Codea, so please excuse the ugly code that is coming.
The code
The goal of the simulation is to have several “bodies” which interact through gravity only. I don’t assume much knowledge of Codea, but if things are not clear there are some great Codea tutorials online.
Let’s start with the class describing these bodies.
Body = class()Our Body object creation method takes the initial position, speed, body mass, and color, and sets some instance variables.
function Body:init(x,y,vx,vy,m,c) -- you can accept and set parameters here self.pos=vec2(x,y) self.speed=vec2(vx,vy) self.mass=m self.color=c endThe
vec2object is a point. It has several interesting methods including one that computes the distance between two points. We make use of it in the next method, wherevis avec2.function Body:dist(v) return self.pos:dist(v) endWe now get to the first method which is about physics: Newton’s second law of motion “F = m . a” (force is equal to mass times acceleration, where acceleration is the rate at which speed changes:
a = (new_speed - old_speed)/time). This method applies a forceffor a given timeton the body, which results on the body’s speed changing.function Body:applyForce(f,t) self.speed = self.speed + t * f / self.mass endSince speed is the rate at which position changes, we can describe how the position evolves during some time t.
function Body:updatePos(t) self.pos = self.pos + t * self.speed endThe last two methods are about the graphical interface. The first one returns the on-screen position of the body. The parameter
zis the zoom factor (how many distance units are in a pixel), andcis the position that is at the center of the screen.function Body:screenPosition(z,c) return vec2((self.pos.x-c.x)*z + WIDTH/2, (self.pos.y-c.y)*z + HEIGHT/2) endFinally, we draw a body as a small circle of the body’s color at the body’s screen position.
function Body:draw(z,c) local position = self:screenPosition(z,c) fill(self.color) ellipse(position.x,position.y,10) endLet’s now move to the next class, which will be at the heart of the simulation.
Bodies = class()An instance of this class is an array of the bodies we want to simulate.
function Bodies:init() self.blist={} end function Bodies:addBody(b) table.insert(self.blist,b) end function Bodies:bodyAtIndex(i) return self.blist[i] endThe central method of the whole program is the next one. Given a body (identified by its position in the array), compute the force the other bodies exert on it. We accumulate this force in
force, and heavily rely on the vector operations (addition or subtraction of vectors, multiplication and subtraction by a scalar) to compute it.More precisely, we enumerate the bodies of the simulation (in the
forloop), and for each one, calledb, we compute the force it exerts onbody. This force is a vector frombodytobwhose length is (according to Newton’s law of universal gravitation)G * m1 * m2 / d^2, wherem1andm2are the masses of the bodies,dis the distance between the bodies, andGis the gravitational constant.The code is almost exactly this formula, except that there is no
G. As I’ll explain later, we choose our units such thatGis equal to 1. The formula(b.pos - body.pos)/dis the vector frombodytobwhose size is 1. We multiply it by the masses of both bodies, and divide by the square of the distance.When we’re done accumulating the force, we simply return it.
function Bodies:computeForce(bnum) local body=self.blist[bnum] local force=vec2(0,0) local d for i,b in ipairs(self.blist) do if i ~= bnum then d = body:dist(b.pos) if d ~= 0 then force = force + ((b.pos - body.pos)/d) * body.mass * b.mass / (d*d) else print("null") end end end return force endWe could optimize the previous method in two ways. We can delay the multiplication by
body.massuntil the end (since it can be factored in each individual force). We can even forget completely aboutbody.massand changeBody:applyForceso that it does not divide the force bybody.mass(as the multiplication and division cancel each other). As it makes the physics less clear in both cases, I decided to avoid these optimizations.The next method is the simulation main loop: for each body, compute the force and apply it to the body. (The astute reader will notice that applying the force changes the speed of the body, not its position. The position will remain unchanged for the computation of other forces and the speed is not used to compute the forces, so the order in which computations occur does not matter.)
A second loop simulates the bodies moving, changing their position according to their speed.
function Bodies:simulate(time) local force for i,b in ipairs(self.blist) do force = self:computeForce(i,b) b:applyForce(force,time) end for i,b in ipairs(self.blist) do b:updatePos(time) end endThe last two methods are for the graphical interface. The first one searches whether there is a body close (less than 20 pixels) to a given screen point.
function Bodies:bodyAtPosition(touch,zoom,center) for i,b in ipairs(self.blist) do if touch:distSqr(b:screenPosition(zoom,center)) < 400 then return i end end return 0 endThe last function draws everything.
function Bodies:draw(z,c) for i,b in ipairs(self.blist) do b:draw(z,c) end endWe can now turn to the
Mainclass, which will let us look deeper into Codea itself.We first set some parameters. They appear as sliders on the side of the screen. The first one controls the speed of the simulation (how much time passes for each simulation step), and the second how much zoomed in we are. (I should use the usual zooming gesture for that, but I haven’t taken the time to do so.)
parameter("Speed",0,1000,10) parameter("Zoom",0,1000,10)We next set some constants. I’ll talk more about this below, but in a nutshell we are choosing the mass of the Earth as mass unit, and 1000 km as distance unit.
SunMassInEarth = 330000 EarthMassInMoon = 81 EarthMoon = 384 -- thousands of kilometers EarthSun = 149600 -- thousands of kilometersThe
setup()function is called once when the program is launched. We use it to add three bodies (the Sun, the Earth, the Moon) at some initial positions (so that distances between them are as expected) and some initial velocity I tweaked to get the orbits to look right (more about this below).-- Use this function to perform your initial setup function setup() bl=Bodies() bl:addBody(Body(0,0,0,0,SunMassInEarth, color(255,255,0,255))) bl:addBody(Body(0,EarthSun,1.5,0,1, color(0,255,255,255))) bl:addBody(Body(EarthMoon,EarthSun,1.5,-0.05, 1/EarthMassInMoon, color(255,255,255,255))) center = vec2(0,0) zfactor = 10000 centeredOnBody = 0 print("Tap on a body to track it") print("Tap anywhere else to stop tracking it") print("Drag to move to view point") endThe
touched(touch)function is called every time a touch event occurs. If the touch has just began, then we check if there is a body near. If so, we remember to track this body (to set the center of the display on this body). If the touch isMOVING, it means that the user is dragging his finger, and we move the center of the display accordingly.function touched(touch) if touch.state == BEGAN then centeredOnBody = bl:bodyAtPosition( vec2(touch.x,touch.y), Zoom/zfactor,center) elseif touch.state == MOVING then center.x = center.x + (lastx - touch.x)*zfactor/Zoom center.y = center.y + (lasty - touch.y)*zfactor/Zoom centeredOnBody = 0 end lastx = touch.x lasty = touch.y endFinally, the
draw()function is automatically called every frame (this is the main loop of the program). There we erase everything, and if the simulation is not paused, we make it progress forSpeedamount of time. If we’re tracking a body, we set the center of the display to this body. Finally, we redraw everything.-- This function gets called once every frame function draw() background(0) if Speed > 0 then bl:simulate(Speed) end if (centeredOnBody ~= 0) then local b = (bl:bodyAtIndex(centeredOnBody)) center.x = b.pos.x center.y = b.pos.y end bl:draw(Zoom/zfactor,center) endI could say “that’s it”, but there is one more thing…
On units of measurement
This simulation tries to be realistic. But except for the mass and distance of the bodies, there is no magic number. In particular, I chose
Gto be equal to 1 instead of it’s real value (6.673 10^-11). How can this work?It works because I never specify anywhere what the time unit is, and mass, distance, and time are independant base units. I thus have 3 degrees of freedom to use, and by choosing a unit of mass (weight of the Earth), a unit of distance (1000 km), and the constant
G(1), I fix the time unit to something we can compute. This is how I did it using Soulver, keeping probably too many digits…# Soulver Input Result 1 In m3 kg-1 s-2, G: 6.673 × 10-11 6.673×10-11 2 In kg, Earth mass (EM): 5.974 × 1024 5.974×1024 3 In m3 EM-1 s-2, G: 6.673×1011 × 5.974×1024 3.987×1014 4 In (1000km)3 EM-1 s-2, G: 3.987×1014/((106)3) 0.0003986 5 thus our time unit is: √(1/0.0003986) 50.08 6 7 Earth speed: 2 × π × 149,6 × 106 × 103/(365 × 24 × 3600) 29806 8 in our units: 29806 × 50.08/106 1.493 9 Moon speed: 2 × π × 384 × 103 × 103/(28 × 24 × 3600) 997.3 10 In our units: 997.3 × 50.08/106 0.04995 As
Gis expressed in “something per kg”, to change it into “something per Earth mass”, we multiply it by the mass of the Earth (line 3). To go from meters to thousands of kilometers, we divide the result by one million thrice (sinceGis in meters cube, line 4). To conclude, let’s callfooour time units, which is a number of seconds. To convertGin this unit, we need to multiply it byfootwice (or byfoosquared), and the result would be 1 (which is what we chose forG):line4 * foo^2 = 1 foo^2 = 1 / line4 foo = √(1 / line4)which is what we compute on line 5. Our time unit is thus 50 seconds. If we call our simulation with a speed of 10, for each frame we simulate 500 seconds.
The last few lines are checks that everything is consistent. In line 7, I compute the Earth orbital speed in meters per seconds. It’s simply going around one orbit
2 × π × 149,6 × 10^6 × 10^3(Earth is 150 million of kilometers away from the sun) in one year. When converting this to our units (thousands of kilometers and fifties of seconds), we get 1.49, which is quite close to the 1.5 I tweaked for initial Earth velocity to get a circular-looking orbit. Similarly, we can compute the orbital speed of the Moon, and find it’s consistent as well.Final words, or tldr
There are many things I want to add to this, such as trails for trajectories, zooming using gestures, working in 3d (both for the simulation, easy, and for the interface, much harder)… But with this very simple program, I was able to get my son interested in celestial mechanics and in the mind-blowing distances in the solar system.
This post was way longer than I expected, and probably too technical. If there is one thing to take away, try the following: Codea is a really cool app, and it’s quite easy and to play with physical simulations in it. And physics, even as simple as this, is fun!
-
2011年08月25日
How many calendars do I need?
I was recently reading this review of the new Calvetica when I was enlightened by this footnote:
Though I am rethinking this, I mean does it really matter if it is a work event or personal event? I still have to attend both…
This small comment made me re-think how I organize my calendars. Previously, it was by areas of responsibility: work, home, teaching, and so on. But I don’t really care if an event is about “work” or “home”, what matters is what the event is (its title), and whether I’m available to attend it. So I now just have one calendar, called Alan. As we share calendars in the family, there is also a calendar for Christelle, one called Family (when both Christelle and I need to be available), one for each of the kids, and a general Info calendar for non-events.
Since switching to this system, I’ve found that it provides enough information without requiring me to think to much when I enter an event: I just need to know whom it concerns.
-
2011年05月24日
Tweeting from LaunchBar
Update (2011年05月28日) I was not correctly escaping the string (just putting in into quotes was not enough if the tweet had quotes). The code below has been updated to use the very useful
quoted form ofAppleScript construct.Following this great post on Hypertext, I’ve been using ttytter with LaunchBar recently to send some quick tweets. I was not 100% happy with the AppleScript used to launch it, so I tweaked it to get some feedback about whether the tweet succeeded, and to warn me if it is too long. Here is the result.
on handle_string(tweet) if length of tweet ≤ 140 then set theResult to do shell script "~/bin/ttytter.pl -status=" & quoted form of tweet & " 2>&1" display dialog theResult buttons {"Dismiss"} default button 1 else display dialog ("tweet too long: " & (length of tweet) & " characters") buttons {"Dismiss"} default button 1 end if end handle_string -
2011年04月12日
Downgrading my iPhone 3G to iOS 3.1.3
I just got tired of waiting. Waiting for apps to launch. Waiting for Apple to release a version of iOS that would not suck. Yes, my iPhone 3G is old hardware, almost 3 years old. But, coming from Apple, I was expecting the experience to grow better through software updates, until the hardware stopped receiving them. As it recently happened (the iPhone 3G did not get the recent iOS 4.3), the hope of things being eventually fixed disappeared. So I just thought back to the best experience I had with my old iPhone, and downgraded to that.
This, of course, boils down to a trade-off. The advantages of iOS 4 for the 3G are fairly small: folder and unified inbox are the only ones that come to mind. But then there are the two big issues in downgrading: apps that require recent versions of iOS, and the actual process which made me start from scratch, thus potentially lose data, as I did not have a 3.1.3 backup.
Let’s first talk about the data, as things turned out quite well. I realized that, with a few exceptions, most of my data is either in the cloud, or live in external applications on my computer. Photos are in iPhoto; music, podcast, and movies in iTunes. Instapaper data is in the cloud, my text files live on Dropbox as well as my 1Password data, OmniFocus is OmniSynced, even Weightbot (a small app to track my weight) can backup to and restore from the web. Book reading is done on the iPad, so I did not even look into what would happen with the Kindle app. To summarize, I had to manually backup SMS and voice messages (using PhoneView), make sure everything was synchronized, and that was it. The only data I knowingly lost is from some games, and the only one I regret is my ongoing game of Dev Story. I’ll have to start a new one, for sure. Beside this, I lost some configuration data (favorite phone numbers, screen organization) which I’m slowly recreating.
As concerns applications, I first went through the ones I have on my home screen to see if they were supported, and most are, with a few exceptions: Calvetica, Notesy, and Soulver. Unexpectedly, I discovered after downgrading that although Elements does not require iOS 4, it does require iOS 3.2 … but there never was a 3.2 for the iPhone, so Elements was also out of the picture. This was a bit bothersome, as until now I needed to use two notes applications: one for a subset of my notes that would load quickly, another for the whole set of more than 1300 (and growing) text files.1 I’m trying to use Simplenote with these many files, and even though it’s not as fast as dealing with the smaller set, it seems to be usable. I’ll see if I have to head back to DrDrang’s great iPhone notes app comparison to find a sidekick for Simplenote.
Another small disappointment occurred when I discovered that the “swipe to turn pages” feature in Instapaper is iOS 4 only. So I’m back to “tilt scrolling” which, while not as convenient, still works quite well.
But beyond these small issues, downgrading just brought my iPhone back to life. I had completely forgotten how responsive it could be. Apps launch instantly, and they even crash less. I used to be very eager to upgrade this phone, as it had grown so sluggish. But now I know I can wait, probably till some iPhone 5 comes along, happily using my blazingly fast iPhone 3G.
iOS 3.1.3: doing less, but doing it well, is so much better.
See this blog post if you want to know how I keep the subset in sync with the larger set of notes. ↩︎
-
2011年04月08日
Markdown with Keyboard Maestro
Following this interesting video on Practically Efficient showing how to quickly write some Markdown markup using LaunchBar and TextExpander, I decided to write a few similar macros for Keyboard Mastro. Here they are.
The first two ones are fairly simple: they make the text bold or italic. To use them, simply select the wanted word(s) and hit the shortcut. Here is how they are defined.
The last one is to create a reference link. As before, select the text for the link, and hit the shortcut. A window will appear asking for the reference to use for the link (by default the highlighted text), and will create the syntax for the link, both around the highlighted text and at the end of the file. The user may then write down the url.
The AppleScript in this macros is as follows.
tell application "System Events" activate set theText to the clipboard display dialog "Edit link name" default answer theText set the clipboard to text returned of result end telland the last inserted text is simply
[%CurrentClipboard%]:These macros could clearly be improved, but I had fun writing them, and I find them more efficient that going the TextExpander route (as I don’t have the extra copy step). I hope you enjoy them!