Whooshing From Chrome to Safari and Back

Chrome works better for me than Safari, particularly when I have many, many tabs open. However, when I run my Macbook on batteries, Chrome drains batteries too fast, so I’d like to use Safari when I’m on battery power. I created a couple of Applescripts to move my open tabs from Chrome to Safari and vice versa.

I found scripts online to do these, but they appear to rely on obsolete editions of OS X. My scripts below work on OS X High Sierra with the current Google Chrome as of 2018.01.01.

Conceptually, it is a simple task: Take these tabs in one browser and open them in the other. (“Whoosh them” from one browser to the other.) There are some challenges along the way:

  • The object model (“dictionary” in Apple parlance) for the browsers differ.
  • The object model is incompletely documented. There are many properties and methods which you just have to discover via Googling.
  • Pinned tabs behave differently between Chrome and Safai. Frankly, the Safari practice of putting your pinned tabs in every Safari window is just stupid.
  • You can’t pin a tab via the object model – you must resort to GUI scripting.
  • Bringing a window or tab to the foreground changes the order of the browser’s windows/tabs, so you can’t iterate through windows/tabs while bringing them to the foreground. You have to collect the IDs and then iterate through the IDs.
  • AppleScript is just an all-around peculiar language and environment.

Here is Chrome-to-Safari:

-- Move all tabs from Chrome to Safari.


-- Optionally close existing Safari tabs first
set question to display dialog "Close exisisting Safari tabs first?" buttons {"Yes", "No"} default button 1
set answer to button returned of question
if answer is equal to "Yes" then
	closeAllSafariTabs()
end if

tell application "Google Chrome"
	
	-- for every Chrome window, open a Safari Window
	repeat with theChromeWindow in windows
		set theSafariWindow to my openNewSafariWindow()
		
		-- for every tab in the Chrome window, open a tab in the Safari window
		repeat with theChromeTab in theChromeWindow's tabs
			my makeSafariTab(theSafariWindow, URL of theChromeTab)
		end repeat
	end repeat
end tell

pinSpecialTabsInSafari()

tell me to activate
set question to display dialog "Close old Chrome tabs?" buttons {"Yes", "No"} default button 2
set answer to button returned of question
if answer is equal to "Yes" then
	closeAllChromeTabs()
end if

tell application "Safari" to activate


-- Open a *new* Safari window.
on openNewSafariWindow()
	tell application "Safari"
		make new document at end of documents
		set theSafariWindow to window 1
	end tell
	return theSafariWindow
end openNewSafariWindow



-- I have a set of sites for which I used pinned tabs in Chrome.
-- Note that because pinning behaves differently in Chrome and Safari, I don't always pin my pin-desired tabs when I move them to Safari.
on pinSpecialTabsInSafari()
	
	-- Note: You can't simply iterate windows and tabs because activating a window/tab re-orders the collection.
	--       You have to get a collection of IDs and iterate the IDs because they don't change.
	
	tell application "Safari" to set windowIds to (id of every window)
	tell application "Safari" to activate -- have to activate Safari because I'm automating clicks on its menus.
	
	repeat with wid in windowIds
		tell application "Safari"
			set theSafariWindow to (first window whose id = wid)
			set index of theSafariWindow to 1 -- bring THIS Safari window to top of Safari.
			
			tell theSafariWindow
				-- if you don't do this, sometimes Safari is the front app, with the desired window in the foreground, but it isn't actually activated.
				set visible to false
				set visible to true
			end tell
			
			repeat with t in every tab of theSafariWindow
				tell theSafariWindow to set current tab to t
				my waitForFrontTabToLoad()
				set theURL to URL of t
				if my shouldPinMe(theURL) then
					my pinActiveTab()
				end if
			end repeat
		end tell
	end repeat
	
	
end pinSpecialTabsInSafari


-- Create a new Safari tab in the specified window.
on makeSafariTab(theSafariWindow, theURL)
	
	if (theURL as string) = "chrome://newtab/" then
		-- skip empty tabs.  (You'd need to change chrome://newtab/ to about:blank in order to open it in Safari.)
	else
		tell application "Safari"
			try
				tell window 1 to set current tab to make new tab at end of tabs of theSafariWindow with properties {URL:theURL}
			on error
				open location theURL
			end try
			
			-- Close empty 'Favorites' tab created when making new window.
			set thisTab to tab 1 of theSafariWindow
			if thisTab's name as string = "Favorites" then close tab 1 of window 1
			
		end tell
	end if
end makeSafariTab


-- Wait for the front-most tab of Safari to finish loading (at least until it has a title).
on waitForFrontTabToLoad()
	repeat until leftString((name of front document of application "Safari"), 8) is not "Untitled"
		delay 0.1
	end repeat
end waitForFrontTabToLoad


-- I always pin certain tabs.
-- Note that because pinning behaves differently in Chrome and Safari, I don't always pin my pin-desired tabs when I move them to Safari.
on shouldPinMe(uri)
	
	tell application "Safari" to set wCount to count of windows
	if wCount > 1 then return false -- pinned tabs are stupid in Safari.  Pinning a tab in one window will add it to ALL safari windows
	
	if (uri as string) = "missing value" then error "no URI in shouldPinMe"
	if uri contains "//pinboard.in" then return true
	if uri contains "//focus.nirvanahq.com" then return true
	if uri contains "//www.nirvanahq.com" then return true
	if uri contains "//workflowy.com" then return true
	if uri contains "//voice.google.com" then return true
	if uri contains "//calendar.google.com" then return true
	if uri contains "//mail.google.com" then return true
	
	return false
end shouldPinMe


-- Pin the tab which is currently front-most in Safari.
on pinActiveTab()
	-- delay 1
	tell application "System Events"
		tell process "Safari"
			set frontmost to true
			click menu item "Pin Tab" of menu "Window" of menu bar 1
		end tell
	end tell
end pinActiveTab


-- Close every Safari tab.
on closeAllSafariTabs()
	tell application "Safari"
		set wCount to count of windows
		repeat with i from 1 to wCount
			set theSafariWindow to window 1
			repeat with theSafariTab in theSafariWindow's tabs
				tell theSafariWindow to close tab 1
			end repeat
		end repeat
	end tell
end closeAllSafariTabs


-- Close every Chrome tab.
on closeAllChromeTabs()
	tell application "Google Chrome"
		set windowList to every tab of every window
		repeat with tabList in windowList
			set tabList to tabList as any
			repeat with tabItr in tabList
				set tabItr to tabItr as any
				delete tabItr
			end repeat
		end repeat
	end tell
end closeAllChromeTabs


-- Return the first n characters of string s.
on leftString(s, n)
	if length of s is less than n then
		return s
	else
		return text 1 thru n of s
	end if
end leftString

Here is Safari-to-Chrome:

-- Move all tabs from Safari to Chrome

-- Optionally close existing Chrome tabs first
tell me to activate
set question to display dialog "Close exisisting Chrome tabs first?" buttons {"Yes", "No"} default button 1
set answer to button returned of question
if answer is equal to "Yes" then
	closeAllChromeTabs()
end if


tell application "Safari"
	repeat with theSafariWindow in windows
		tell application "Google Chrome" to set theChromeWindow to make new window
		repeat with theSafariTab in theSafariWindow's tabs
			set theURL to URL of theSafariTab
			tell application "Google Chrome"
				set theChromeTab to make new tab at end of tabs of theChromeWindow
				set URL of theChromeTab to theURL
				
			end tell
		end repeat
		-- Close empty tab created when making new window.
		tell application "Google Chrome" to close tab 1 of theChromeWindow
		
		
		tell application "Google Chrome" to set tabCount to number of tabs in theChromeWindow
		repeat with i from 1 to tabCount
			tell application "Google Chrome" to set active tab index of first window to i
			tell application "Google Chrome" to set theURL to URL of active tab of front window
			if my ShouldPinMe(theURL) then
				my pinActiveTab()
			end if
		end repeat
		
	end repeat
end tell


tell me to activate
set question to display dialog "Close exisisting Safari tabs?" buttons {"Yes", "No"} default button 2
set answer to button returned of question
if answer is equal to "Yes" then
	closeAllSafariTabs()
end if


-- I always pin certain tabs.
on ShouldPinMe(uri)
	if uri contains "//pinboard.in" then return true
	if uri contains "//focus.nirvanahq.com" then return true
	if uri contains "//workflowy.com" then return true
	if uri contains "//voice.google.com" then return true
	if uri contains "//calendar.google.com" then return true
	if uri contains "//mail.google.com" then return true
	return false
end ShouldPinMe

on pinActiveTab()
	tell application "Google Chrome" to activate
	tell window 1 of application "Google Chrome" to set visible to true
	tell application "System Events"
		tell process "Google Chrome"
			set m1 to (first menu bar item where name is "Window") of menu bar 1
			set mx to menu item "Pin Tab" of menu 1 of m1
			click mx
		end tell
	end tell
end pinActiveTab


on closeAllChromeTabs()
	tell application "Google Chrome"
		set windowList to every tab of every window
		repeat with tabList in windowList
			set tabList to tabList as any
			repeat with tabItr in tabList
				set tabItr to tabItr as any
				delete tabItr
			end repeat
		end repeat
	end tell
end closeAllChromeTabs


on closeAllSafariTabs()
	tell application "Safari"
		set wCount to count of windows
		repeat with i from 1 to wCount
			set theSafariWindow to window 1
			repeat with theSafariTab in theSafariWindow's tabs
				tell theSafariWindow to close tab 1
			end repeat
		end repeat
	end tell
end closeAllSafariTabs

How to Enter Special Characters on a Mac Keyboard

My wife was chatting with someone who asserted that he could not enter a ‘#’ because he’s using a Mac. He later refined his argument to be because he’s using a British Mac and his shifted-3 is a ‘£’. Finally, he refined his argument to be because he couldn’t be bothered to put forth the effort.

Spoiler: On British/American keyboards, use Shift-3 or Alt-3 to get # or £.

This led me to wonder how one enters special characters on a Mac. I’m familiar with the system character chooser on Windows. How does Mac do this?

One-time setup:

  • Go to ‘System Preferences’ and open the Keyboard applet. Select the ‘Input Sources’ tab.
  • Using the ‘+’ button, add any missing items from the following list:
    • British
    • Dvorak (If you use it. I do.)
    • U.S.
    • Unicode Hex Input
  • If you have trouble locating any of the items above, use the Search field.
  • Be sure to put a checkmark in the “Show input menu in menu bar”. This will add an icon to you menu bar.
    • Be sure you know which one it is. You can toggle the checkmark off and on to check which icon.
  • This is optional, but spiffy:
    • Select any one of the keyboards which you’ve added with the ‘+’. Mac will display that layout to the right. Ho hum.
    • Press and hold the Shift key. Mac will show you the shifted form of the layout.
    • Press and hold the Alt key. Mac will show what happens if you enter the Alt form of that keystroke.
    • You can do this for any of the installed keyboard layouts.

Using alternate keyboard layouts

One approach:

  • Click the input menu in the menu bar. (Mine says “DV” because I use Dvorak. Yours is probably the flag for your country.)
  • Choose “Show Keyboard Viewer”. You’ll see a keyboard image.
  • Hold down Alt to see the alt form of each key.
  • If you see the one you want, press it!

Another approach: This one works for esoteric keystrokes.

  • Click the input menu in the menu bar. Choose “Show emoji and symbols”.
  • Browse through the menus to find the symbol you want and double-click it to ‘type’ that symbol. 😀

Setting Up Eclipse

Setting up Eclipse for Java, Python, and Clojure

  • Ensure you have Java 8 JDK (run “javac -version”)
  • Download Eclipse from https://www.eclipse.org/downloads/
    • Select “Download Packages” and install “Eclipse IDE for Java Developers”.
    • I got the “Oxygen 1a” version.
  • Launch Eclipse
    • I set my workspace to ~/Sync/Code and I made it my default location.
    • Help > Eclipse Marketplace > search for pydev. Install pydev (all features). Restart when asked.
    • Help > Eclipse Marketplace > search for StartExplorer. Install. It is unsigned. Restart when asked.
    • Help > Eclipse Marketplace > search for counterclockwise. Install. It is unsigned. Restart when asked.
  • Note: It takes about 8 seconds to launch before installing Counterclockwise. CC adds another 3-4. Slow for an editor, but OK for an IDE.
  • General Eclipse Configuration:
    • In the upper-right corner, to the right of “Quick Access”, you’ll see some icons. Right-click on any of them except for the first one. Choose “Show text”. These icons allow you to quickly switch Eclipse “perspectives.”
    • Install “Eclipse Moonrise UI Theme” via Help > Eclipse Marketplace.
    • Install Eclipse Color Theme plugin.
    • Preferences > General > Appearance. Select “Appearance” and set the theme to “Moonrise (standalone)”. There is a “Color Theme” in the hierarchical menu. Don’t use that one for this step. Choose Appearance and ON THAT TAB select the theme.
    • Preferences > General > Appearance > Color Theme. Select Color Theme from the hierarchical menu; not on the Appearance tab. Select “Vibrant Ink” color theme. You have to do both themes in order to get the ancilliary panes to be dark. Vibrant Ink only colors the editor.
    • I was getting black on black in the Package Explorer.
      • Preferences > General > Appearance > Colors and Fonts. Search for “Uncommitted Change (Foreground)”. I set it to FFADA8. This fixed some nodes in Package Explorer. Editing “Ignored Resources (Foreground)” got the others.
    • Still about 12 seconds to launch Eclipse…
  • Clojure Eclipse Configuration:
    • Preferences > Clojure > Editor >
      • Editor text autoshift
      • Highlight matching brackets
      • Displayed tab width = 2
      • On file launch, switch the Repl…
      • Start editors in strict/paredit mode
      • DO NOT: Escape text when pasting…
      • Tab reindents the current line
      • Auto activate code completion
      • Display namespace in tabs instead of file name
    • Preferences > Clojure > General. Turn OFF “Launch REPLs with cider-nrepl”. Otherwise, your REPL will just die for no reason, after being idle for a few minutes and at random times. It is a known problem.
    • Mac users - REPL command history is via Ctrl-UpArrow and Ctrl-DownArrow. These have Mac meanings, so redefine them to use Cmd instead of Ctrl:
      • Preferences > General > Keys. Search for “command from repl”.
  • Eclipse elements to be familiar with:
    • Java perspective
      • Package Explorer - Left panel. Browses the file system.
        • To launch a REPL without loading a file: Select the project in the Package Explorer; Run > Run Configurations > Clojure > (pick a configuration) > Run.
          • Source code - Main panel in the middle. IF a repl is running, you get fly-over syntax help. (Clojure > Load file in repl. There are other ways to launch a repl, but you don’t seem to get fly-over unless you load the file in the repl.)
          • Outline (a.k.a. namespace browser) - Right panel. Lists the functions in the currently selected source file. Use the sort icon at the top, to choose between show-in-alpha-sequence and show-in-source-file-sequence.
          • Bunch-of-views-at-bottom-of-page: There are several here in a single panel.
            • The ones I find useful:
              • Console - shows output from println.
              • REPL
            • The ones I closed:
              • Problems, Javadoc, Declaration

Things to do or look into:

  • Alt-L brings up a Leiningen menu. You can Launch Headless REPL. You can run ‘lein anything here’ in the current project directory.
  • You can generate a new Clojure project via File > New > Clojure Project, but it uses the ‘default’ project, which is a library. You probably want to drop to the command prompt and use lein new app app-name-here
  • When you generate a new Clojure project from Eclipse, it appears to always put clojure 1.6 in the generated project.clj. Find this line in project.clj and set it to the Clojure version you are using: :dependencies [[org.clojure/clojure "1.6.0"]]
  • Note: It takes almost 20 seconds to launch a Clojure repl in Eclipse. Can I speed that up?
  • Refactor this page, to move getting-started-with [Java/Python/Clojure]-in-eclipse to separate pages.
  • Look into Clojure JUnit integration via https://github.com/mikera/cljunit

Java Hello World With Visual Studio Code and Eclipse

Hello World in Java with Visual Studio Code

  • Download and install a JDK. (Look for “Java SE Development Kit”.) If you don’t get an error when you type “javac -version” at a Command Prompt (Terminal window in Mac), you already have a JDK.
  • Download and install Visual Studio Code (VSCode) from https://code.visualstudio.com/download
  • Launch VSCode
  • Tell VSCode to create a new file via File > New
  • Paste the following text
public class test1 {
    public static void main(String[] args) {
        System.out.println("Hello, World");
        System.out.println("Goodbye, World");
    }
}
  • File > Save As > test1.java
    • VSCode will prompt you to install the Java extension pack. Do so. It will take a few minutes.
      • When it finishes installing, it will show a “Reload” indicator. Click it.
      • It might give you a “Classpath is incomplete” warning.
        • This happens every time you work with a stand-alone Java file (i.e. a Java file that is not part of a project.)
        • For now, just dismiss the warning.
  • View > Integrated Terminal
    • cd to the directory where you saved test1.java
    • javac test1.java
      • ‘javac’ is the compiler. ‘java’ is command which used to run a compiled program.
    • Do a directory listing. Observe that javac compiled test1.java to test1.class.
    • java test1
      • It should display “Hello, World”

Note that you will almost never create a Java program this way. This is a special process for a single-source-file Java app. Typically, your app will be comprised of many files, and you’ll have to create a ‘project’ to tie them all together. Most Java projects are godawful complex things which require a ‘build tool’ to compile and assemble them into something you can run. There are multiple build tools which Java developers use because developers often say, “This is awful. I could build something better.”

We’re going to use Maven. Maven is good for our purposes because VSCode works with it and so does Eclipse, in case you later decide you have to suffer Eclipse. Maven revolves around a ‘POM file’ (Project Object Model), which is written in XML.

We’re going to abandon this stand-alone source file and create a hello-world Maven project; we’ll also set you up to use the Java debugger.

  • Close VSCode.
  • For Mac
    • Run “brew install maven”. It will install to /usr/local/bin/mvn.
    • Run mvn --version
    • Add this to .bash_profile, substituting the value for Maven home as reported by mvn (above).
      • export M2_HOME=/usr/local/Cellar/maven/3.5.2/libexec
      • export M2=$M2_HOME/bin
      • export PATH=$PATH:$M2_HOME/bin
  • For Windows, download and unzip Maven from http://maven.apache.org/download.cgi.
    • You want a “Binary zip archive”. Choose the link from the “Link” column to start the download.
    • Store it in a directory all by itself. Maybe name that directory “MAVEN”.
    • Set a M2_HOME environment variable to point to the MAVEN directory.
    • Set a M2 (not M2_HOME - just ‘M2’) environment variable to point to the bin subdirectory of the MAVEN directory.
    • Add the M2 (bin) directory to your PATH.
  • In your Terminal window (Command Prompt), navigate to an empty directory, where you wish to create your Java project.
    • mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
      • This may take a long time.
    • cd to my-app/src/main/java/com/mycompany/app. (If you’re on Windows, your slashes lean the other way.)
    • Run a directory listing and you’ll see App.java. Examine the contents. You’ll see that Maven created a hello-world app for you. This godawful directory structure – that’s how Java apps are built.
  • Launch VSCode and open the my-app directory from the File menu.
    • In the left panel of VSCode, navigate to my-app/src/main/java/com/mycompany/app, and open App.java
    • Find the line with System.out.println. Left-click with your mouse, just to the left of the line number. You should see a red circle. You’ve just set a breakpoint on this line.
    • Choose Debug > Start Debugging from the menu. If it asks what kind, select Java.
    • It will highlight the line with your breakpoint. It executed all the code before that point, and stopped.
    • Choose Debug > Step Over. It will execute your println, and you’ll see the output displayed in the bottom panel.
    • Choose Debug > Continue. It will ‘execute’ all those closing braces and terminate your program normally.

Hello World in Java with Eclipse

Install and Launch LiClipse

We’re going to use LiClipse, which is Eclipse bundled with some other tools.

  • Download and install a JDK. (Look for “Java SE Development Kit”.) If you don’t get an error when you type “javac -version” at a Command Prompt (Terminal window in Mac), you already have a JDK.
  • Download and install LiClipse from http://www.liclipse.com/download.html
  • Launch LiClipse (henceforth to be referred to simply as Eclipse).
  • Eclipse wants to use a “workspace”. That’s the root folder for all projects you develop using Eclipse. Pick one that suits you. On my Mac, I use /Users/kevin/Sync/code. Unless you want it to nag you each time you launch it, select “Use this as the default and do not ask again”.
  • Help > Install New Software > select “Eclipse x.x Release…”
    • In the “Type Filter Text” field, type “java” and press Enter.
    • “Select Eclipse Java Development Tools” and press Next; then work you way through the Wizard until it is installed.
    • Watch the progress dialog in the lower-right corner of Eclipse. Don’t proceed until it hits 100%. It may be sloooow.
    • When it wants to restart Eclipse, let it.

Create and Run a Single-file Java App

You really, really have to put your single-file Java app in a project. If you don’t, you’ll find yourself unable to save your file. Eclipse understands projects. It doesn’t really deal with stand-alone files.

  • File > New > Project > Java > Java Project. Press Next.
    • Enter a project name of test1.
    • Choose “Use Project folder as root for sources and class files”
    • Press Finish.
    • It will natter about opening a Java Perspective. Let it, and tell it to always do so. It is just going to open the panels which are relevant to Java.
  • File > New > Class
    • Source folder = test1
    • Package = (empty)
    • Name = test1
    • Superclass = (empty)
    • Clear ALL checkboxes
    • Press Finish
  • Make the test1.java file contain:
public class test1 {
    public static void main(String[] args) {
        System.out.println("Hello, World");
        System.out.println("Goodbye, World");
    }
}
  • File > Save
  • Locate the toolbar icon for “Run”. (One of the icons with a green circle with a triangle.) Press it.
    • It will natter at you about Run Configurations.
      • Double-click “Java Application” and choose test1 (under Java Application).
      • Press the Run button.
    • Notice your output at the bottom of the page.

Note that you will almost never create a Java program this way. This is a special process for a single-source-file Java app. Typically, your app will be comprised of many files, and you’ll have to create a ‘project’ to tie them all together. Most Java projects are godawful complex things which require a ‘build tool’ to compile and assemble them into something you can run. There are multiple build tools which Java developers use because developers often say, “This is awful. I could build something better.”

We’re going to use the built-in Eclipse build too. If you ever need to do so later, Eclipse can also work with Maven projects and it can export Eclipse projects into Maven ‘POM files’. Maven revolves around a POM file (Project Object Model), which is written in XML.

We’re going to abandon this stand-alone source file and create a new hellow-world project; we’ll also use the Java debugger.

  • File > New > Java Project. Note that because you are already in the Java Perspective, Eclipse has hoisted ‘Java Project’ into the top-level menu. You can get the same effect via File > New > Project > Java > Java Project
    • Project Name = my-app2
    • Use default location
    • JRE: use whatever it defaults to
    • Project Layout: Create separate folders…
    • Press Finish
  • Stop and look.
    • You should now see TWO Java projects in the Package Explorer panel at the left side of Eclipse.
    • Sometimes, if one Java project is not causing you enough pain, Eclipse figures you might want to work on multiple projects at the same time. Getting started, one at a time is enough.:
      • Select my-app2. Then right-click it and choose “Close Unrelated Projects”. Tell Eclipse that you really meant to, when it asks.
      • You will still see a single folder for test1, even though it is closed. That’s how Eclipse works.
  • Select the src folder under my-app2.
  • Right-click src and create a new package:
    • Name it com.mycompany.app and press Finish.
  • Right-click com.mycompany.app and create a new class:
    • Source folder: my-app2/src
    • Package: com.mycompany.app
    • Name: App
    • Modifiers: Public
    • Superclass: java.lang.Object
    • Select “public static void main…” but leave the other checkboxes empty.
    • Press Finish
  • Take a look at the generated App.java file. It is almost a complete hello-world. Replace the TODO comment with:
        System.out.println("Hello, World");
        System.out.println("Goodbye, World");
  • File > Save
  • Locate the toolbar icon for “Run”. (One of the icons with a green circle with a triangle.) Press it.
    • It will natter at you about Run Configurations.
      • Double-click “Java Application” and choose test1 (under Java Application).
      • Press the Run button.
    • Notice your output at the bottom of the page.
  • Double-click the gutter, to the left of the line number by the first line with System.out.println. It will add a small dot to indicate that you have set a breakpoint on this line.
  • Locate the toolbar icon of an insect and the flyover help “Debug App”. Press it.
    • Eclipse will ask permission to switch to the Debug Persipective. Approve it.
    • It will highlight the line with your breakpoint. It executed all the code before that point, and stopped.
    • Choose Run > Step Over. It will execute your println, and you’ll see the output displayed in the bottom panel.
    • Choose Run > Resume. It will ‘execute’ all those closing braces and terminate your program normally.
  • Note that if you ever find Eclipse in the ‘wrong’ Perspective, you can change Perspective via Window > Perspective > Open Perspective.

Programming Languages for the Occasional Programmer

Work in progress (a ‘living document’):

I’m an occasional programmer. I might code intensively for a few months, then do other things, then return to programming. I don’t get to spend all day, every day, coding on any project. I need to be able to productively pick up a project after having set it aside. I need to be able to program in an environment without being intimately familiar with every nook and cranny of the environment.

I need to be able to write code for Windows, Mac, and web (client and server side), and Linux server administration. I have an interest in data science, so the ability to work there would be helpful. Of necessity, a discussion of languages will also include a discussion of platforms related to those languages.

Fewer languages is better than many languages. I’ve dabbled in many. The fewer I have to try to stay current in, the more time I can spend coding, rather than re-learning.

Popularity

Current popular multi-platform general purpose languages (in many rankings):

  • JavaScript - way, way most popular at GitHub
  • Java
  • Python
  • Ruby
  • Much lower frequency: Go, C, TypeScript, Scala, Clojure, R, Perl, Julia, Haskell

Clojure and ClojureScript

Conceptually, I like Lisp. In reality, I run into a problem with un-typed languages. After I pass a string into a routine expecting an integer, and it bubbles down through a half-dozen layers before it blows up, and I spend a long time tracking down where the defect was injected (as opposed to where it was detected), I then start writing code to check the type of actual parameters. Checking type at runtime is stupid; it is more effective to check at compile/load time. An occasional programmer really needs typing of formal parameters. clojure.spec (requires Clojure 1.9, released December 2017) is the Clojure way to check this at runtime.

Clojure/ClojureScript solves the problem of one language for client and server.

I find coding in Clojure to be pleasant and rewarding, but the moment I start trying to test/run the code, it gets real frustrating, real fast, because trivial changes result in run… damn… run… damn… run… cycles.

Slow startup is a problem for some categories of scripts. e.g. If you wanted to write a utility like ‘cat’ or ‘more’.

Clojure can be annoyingly pure sometimes:

  • It won’t let you prematurely exit a function. Often you could remove a level of nesting from a function if you could code something at the top of the function like “if argument is bad then return nil”.

Debugging - You lose the strict source line to what-is-happening-now relationship due to lazy eval. This is worse in ClojureScript, where the compiler minifies the JavaScript

User-hostile error messages.

And Clojure doc is written for the person who already knows the answer. (I’m not the only person to notice this.)

I think the Clojure ecosystem might be really appealing if I coded in it all day, every day. It appears to serve the SME really well.

I did about 6 months of part-time programming in Clojure, writing an app to rebalance my complex investment portfolio.

Ruby

Ruby is a pleasing language for small projects. I really Matz’s notion that using the language should please the programmer.

In common platforms such as Rails, there is so much magic happening behind the scenes that it takes me a couple of weeks to refresh myself on what is really happening, every time I pick it up.

The absensce of typed formal parameters leads me back to run-type type checking. An occasional programmer really needs typing of formal parameters.

Ruby really has to be one of multiple languages. You can’t really do your browser UI in Ruby.

The gyrations (tools) necessary in order to effectively develop with multiple versions of Ruby on a single system is off-putting.

Java

If you’re going to organize an expedition to Mt. Everest, you need lot of supplies and lots of logistics. If you’re walking to the corner bodega, you could apply the same level of planning and effort, but you’ll probably just put on your shoes and walk to the corner. Java is really a good tool for the programming equivalent of an Everest expedition. It is probably the tool you want if you’re going to have 100 people working to create a mega-app. But it applies the same level of congitive overhead when you build a little utility. Java makes EVERY trip an expedition.

Someone captured the essence of Java nicely. Java is the COBOL of the 21st century. Verbose. No fun to work in.

Yes, it does have typed formal parameters. Yes, you can write Java that compiles to JavaScript to run in the browser. Yes, you can write cross-platform GUI apps in Java.

Slow startup is a problem for some categories of scripts. e.g. If you wanted to write a utility like ‘cat’ or ‘more’.

I don’t want to write in Java, and, frankly, I don’t like running Java apps. Even if you use something to give you a native look and feel, they still feel like Java apps.

Python

I want to like Python. I don’t quite. I can live with the indentation thing. Maybe I just need to do more coding with it.

Python 3.6 does support optional typing of formal parameters. I like the concept of optional typing, if all of the published libraries come with typing. I want me to be able to dispense with typing while hacking, but I want to see typing whever I use someone else’s code, and I want to throw a switch and require typing when I begin to production-ize my code.

Pyjamas, Brython, Skulpt, PyPy, Transcrypt: might let you write your browser-side code in Python. Need to check which of those actually works on iPhone and Android browsers.

It runs on almost all platforms. It is used in data science (behind R in popularity, of course).

“Batteries included” is an effective philosophy. “There’s only one right way to do it,” chaps my butt. As philosophers, I like Matz and I’m not sure I like Guido. (Guido may be a fine person to know; I’m just referring to his programming philosophy.)

It doesn’t really fit well with functional programming, although you can bend it to your will. I wish it supported a flag to say “make data immutable.”

Global Interpreter Lock - only one thread per interpreter can be active at a time.

Python has similar virtual-environment issues as Ruby, when you need to develop with multiple different versions.

JavaScript, TypeScript

JavaScript has the appeal of one language for client and server sides. Yes, JavaScript has good stuff, but you have to know which pieces to avoid using. TypeScript supports optional types, solving my need for typed formal parameters (except that many JavaScript libraries don’t come with types).

Electron… I want to use Electron, without the footprint of electron. File size I can live with; huge RAM use, not good; high CPU use, exhausts my battery. Shucks, with the Chromium footprint, I’m reminded that I have to switch from Chrome to Safari on my Macbook whenever I go on battery power.

If you get node.js involved, slow startup is a problem for some categories of scripts. e.g. If you wanted to write a utility like ‘cat’ or ‘more’.

If you’re going to use JavaScript, use a lint.

I love this quote

The thing is, there is a mass psychosis about JS and it’s like everybody is pretending that it isn’t awful.

And this one: “According to Namcook Analytics (Table 16), JavaScript is one of the least productive programming languages in the world (measured by “economic productivity” in terms of number of work hours to deliver 1,000 function points)”

Go (golang)

Statically typed. Simple by design. Fast compile. Quick learn.

Verbose. Because it chooses to leave things out of the language, developers must create them.

“The language could be described as C with training wheels.”

I love this quote from movio: “For the first time ever, we actually read the language spec when we’re unsure of how something works. That’s how simple it is; the spec is readable! For my average-sized brain, this actually means a lot. Part of my frustration with Scala (and Java) was the feeling that I was never able to get the full context on a given problem domain, due to its complexity.”

Another good quote: “if your main focus is around data science you might be better off with the Python stack.”

Scala

Functional. Strongly typed.

SLOW compile. Slow JVM startup for simple apps.

Rumored to have a somewhat hostile community.

Haskell

?

Rust

“Rust is a programming language that’s focused on safety, speed, and concurrency.” It is a systems programming language. Think of it as C, with functional features and memory safety.

Static typing; inferred types. By default variables are immutable. Functional.

In function signatures, you must declare the type of each parameter.

x = y may invalidate y; use x = y.clone() if you really want a copy. Only one reference to heap data at a time.

Smalltalk

This has some interesting assertions: https://medium.com/smalltalk-talk/smalltalk-s-proven-productivity-fe7cbd99c061

Pascal

Yeah, other than Delphi, nobody really programs in Pascal anymore. I really loved that “train track” syntax diagram. Pascal was the last language where I really felt, “I know every iota of this environment.”

VB5

Yeah, it is a dead language and Windows-only. It was really a spiffy tool for exploration. You could change code on the fly, half-way through a function, and continue execution. You could explore the methods of an object at run-time. Lots of support for the occasional programmer.

The language itself was not real interesting. The built-in bugs were frustrating. The environment support for tinkering has never been surpassed.

Bugs

Interesting chart: https://medium.com/javascript-scene/the-shocking-secret-about-static-types-514d39bf30a3 shows bug density (bugs per LOC). Lowest 5:

  • Clojure (lowest)
  • Go
  • Erlang
  • Ruby
  • Scala

Clojure, Go, Erlang, and Ruby are about 1/2 the density of Scala. Everything else is worse. Python and Java are similar at about 4x the level of Go.

The article also says, “You want to reduce bugs? Use TDD. You want useful code intelligence tools? Use static types.”

Most Loved

Per https://fossbytes.com/most-loved-and-most-hated-programming-languages/ , the most loved languages are:

  1. Rust
  2. Smalltalk
  3. TypeScript
  4. Swift
  5. Go
  6. Python
  7. Elixir
  8. C#
  9. Scala
  10. Clojure
  11. JavaScript
  12. F#
  13. Haskell
  14. SQL
  15. C++
  16. Julia
  17. Java
  18. R
  19. Ruby
  20. C

And the most hated:

  1. Visual Basic 6
  2. VBA
  3. CoffeeScript
  4. VB.NET
  5. Matlab
  6. Objective-C
  7. Assembly
  8. Perl
  9. Lua
  10. Hack
  11. Groovy
  12. Common Lisp
  13. Dart
  14. Erlang
  15. PHP
  16. C
  17. Ruby
  18. R
  19. Java
  20. Julia

Windows Batch File Format Date as YYYY.MM.DD

I often need a variable containing a yyyy.mm.dd date in a Windows batch file. Instead of figuring it out anew each time, use this code:

REM --- Get current date into yyyy.mm.dd format.
REM
REM NOTE: You really must use the "if not defined" in order to skip the trailing blank line.

REM clear tmpDate
set tmpDate=

REM This gets the date in a locale-independent format.
REM e.g. 20171130101642.469000-300
for /f "skip=1" %%x in ('wmic os get localdatetime') do if not defined tmpDate set tmpDate=%%x


REM Extract from tmpDate:
REM    position 0 for 4 chars
REM    position 4 for 2 chars
REM    position 6 for 2 chars
REM Adding "." between them
set YYYYMMDD=%tmpDate:~0,4%.%tmpDate:~4,2%.%tmpDate:~6,2%

echo YYYYMMDD is %YYYYMMDD%

Cygwin ssh Daemon How-to, 2017

Enabling the cygwin ssh daemon has changed over the years. Here’s my 2017 edition of a how-to (howto).

  • Run the Cygwin setup and select openssh.
  • open Cygwin64 Terminal (run with ADMIN)

ssh-host-config

tell it:

  • strict modes = no
  • new local account = no
  • yes, install the service
  • CYGWIN = (empty. it is no longer needed)
  • User ID to use = your-personal-Windows-user-ID. (If you use a non-admin and an admin user ID, enter the admin one). If you let the config create a service account, that ID will NOT be able to access network shares. I really want to be able to access network folders when using Unison or rsync!

cygrunsrv.exe –stop sshd /usr/sbin/sshd.exe -D

Solve any error messages. If Windows firewall asks, permit the access. Test your connection. Run ssh from a remote machine and ensure you can connect.

^C cygrunsrv.exe –start sshd

Note: To totally start setup over, first you must:

cygrunsrv –stop sshd cygrunsrv –remove sshd

IF you have an /etc/passwd, delete any sshd or cyg_server user ID.

net user sshd /delete net user cyg_server /delete rmdir /var/empty


Note: Start/stop daemon with:

cygrunsrv –start sshd cygrunsrv –stop sshd

Markdown Toolset

Summary: GitHub Flavored Markdown, kramdown, Marked 2, Typora

I’ve been using a hodgepodge of Markdown tools. I’d like to try and make sense of what I’m using and why.

I’m using Jekyll and GitHub pages (GHP) for my blogs. Jekyll and GHP use the kramdown parser. Per GitHub: “GitHub Pages only supports kramdown as a Markdown processor” and “we’ve enabled kramdown’s GitHub-flavored Markdown support by default.”

See:

  • https://help.github.com/articles/configuring-jekyll/
  • https://kramdown.gettalong.org/parser/gfm.html

So the question of which Markdown flavor to use is either:

  • GFM
  • kramdown

For now, unless I encounter a compelling reason to use native kramdown, I’m using GFM because it is the default on GHP and on GitHub issues.

Other than my blogs,I am the chief consumer of my Markdown documents. I do more reading that authoring. Consequently, I’m less interested in side-by-side (source + rendered) tools than many Markdown fans. I mostly want WYSIWYG editing – a simple WordPad-like (or TextEdit-like) experience for rich text documents. For documents with lots of embedded images or documents where I need precise page layout, I don’t use Markdown.

Mandatory Markdown Features

  • Core Markdown
  • Tables (with some kind of table editor - not just source editing of pipes and spaces)
  • MathJax
  • YAML front matter. Either ignore it, or give me some way to view/edit it.

Summary of Current Tools

  • Github Flavored Markdown: Rationalle explained above.
  • GitHub Pages - Blog publishing: I moved to GHP after my dynamic web site was compromised, and I decided I wanted simple, secure blog hosting.
  • Jekyll - Local blog preview: Since I publish with GHP (which uses Jekyll), I preview locally with Jekyll.
  • Markdown Parser: kramdown. It is what Jekyll uses.
  • Marked 2 - Document viewer (Mac only): A first-rate Markdown renderer. It supports the use of custom Markdown parsers. Natively, it supports Discount for GFM. Someday, I’ll get around to configuring it to use kramdown plus options to make it totally GHP-compatible.
  • Typora - WYSIWYG Editor: I really want a single-pane GUI editor. I prefer one that works on Windows and Mac. The primary candidates are Texts.io and Typora. Texts is “based on Pandoc”. Typora clearly states that it supports GFM. I prefer the non-ambiguous flavor. Texts rewrites perfectly good hand-edited Markdown. Typora less so. I prefer the “full GUI” approach of Texts, but the you-type-Markdown-you-get-WYSIWYG approach of Typora isn’t so bad, and it still leaves me viewing a single-pane rendered document. Texts and Typora are both available for Windows and Mac. Typora also supports Linux. I use all three. (Mac is my primary OS.)
  • none - side-by-side editor: I think there may be cases where I really want side-by-side editing (although I haven’t encountered them yet).
    • Haroopad and MacDown look feasible on the Mac, except see Haroopad YAML problem. Haroopad also supports Windows.
    • I’ve seen some non-Haroopad sites say that Haroopad supports GFM and MathJax. MacDown can be configured to support GFM per https://macdown.uranusjr.com/faq/#gfm , and MacDown supports MathJax.
    • Another candidate is Atom. (markdown-preview-kramdown plugin just doesn’t work right!) I already use Atom for Clojure development.
    • This is an online option: https://kramdown.herokuapp.com/
    • IF I decide I need this, MacDown looks best.

YAML

  • In YAML front matter, if you need a comment, use space-#. If you begin a line with a #, most tools treats that as a title, even in front matter.
  • Marked 2 allows you to strip front matter before rendering. [good]
  • Typora put front matter in a gray box and uses typewriter font. [best]
  • MacDown has a “Detect Jekyll front-matter” option, and puts it in a table. [OK]
  • Haroopad treats front matter as Markdown. [unacceptable]
  • kramdown.herokuapp.com - treats front matter as Markdown. [unacceptable]

Hacks

  • kramdown.herokuapp.com seems to require a blank line between a title and a bullet list. This is reportedly common. My other tools render this as desired. I need to remember to add the blank line after the title.
  • Consider using lint - https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md#md013—line-length
  • Write portable Markdown - http://brettterpstra.com/2015/08/24/write-better-markdown/
    • Use an empty line:
      • after headlines
      • between paragraphs
      • before/after code/verbatim blocks
    • Use spaces after list markers *, -, +, \1
    • Use a space after the header marker # or ## or ###
    • Don’t put blank lines in your lists. It is ambiguous as to whether that starts a new list.
    • You can use blank lines above paragraphs within lists. Just follow the last paragraph immediately with another list item (or the end of the list). e.g.
* list item 1

    paragraph in list item 1
* list item 2
* Empty lines in block quotes are handled differently between flavors as well. The most common way to make a multi-paragraph block quote is to use a greater than symbol on each blank line:
> paragraph one
>
> paragraph two
>> nested paragraph
>>
>> nested paragraph two
* Use ATX Headers (i.e. hashmarks).
* four-space indentation is recognized across the board; when creating nested lists, always use four spaces instead of two.
* For code blocks, use \`\`\` and not \~\~\~ because they are more universal
*

Moving Jekyll to Docker

a.k.a. Docker Jekyl and Mr. Hyde. (Sorry, I couldn’t resist.)

At this point, the only Ruby thing I’m using on my Macbook is Jekyll. Instead of installing an up-to-date ruby, chruby, bundler, and ruby-build (which was how I’d previously run Jekyll). This is the story of how I migrated my Jekyll sites (kleinfelter.com and k4kpk.com) into Docker containers.

 

Notes:

  • My local copy of my primary web site lives in the directory ‘kleinfelter.github.io’.

  • I launch my Jekyll via a Launch Agent, which runs the shell script runme-local.sh in the site directory.

  • I have a newer, canonical form for using Docker.

Steps:

  • Install Docker for Mac.

  • Since I’m auto-starting Jekyll via a Launch Agent, I need to stop the existing one:

    • launchctl unload /Users/kevin/Library/LaunchAgents/com.kleinfelter.jekyll.kleinfelter.plist
  • Since I’m going to use the jekyll/jekyll image, I can stop using bundler. I’m not going to uninstall bundler, since that’s part of my Macbook’s ruby installs, but since I’m going to be using the gems provided in the image, I don’t need to be coordinating my own gems.

    • cd kleinfelter.github.io

    • git rm Gemfile

    • git rm Gemfile.lock

    • git rm -r vendor

    • rm -r vendor

  • Create a docker-compose.yml in kleinfelter.github.io containing:

jekyll-kleinfelter:
    build: .
    command: jekyll serve --watch --incremental
    ports:
        - 4000:4000
    volumes:
        - /Users/kevin/Sync/Sites/kleinfelter.github.io:/srv/jekyll
  • Strictly speaking, docker-compose is about running multiple containers. However, you can use it to run a single container, and it makes the command line for that container simpler, by allowing you to put some of your options in the docker-compose file. This config says:

    • Service is named ‘jekyll-kleinfelter’

    • Build per the ‘Dockerfile’ in the current directory.

    • Launch the process to run in the container with: the given ‘command’ line.

    • Connect host port 4000 to container port 4000.

    • Mount the given host volume onto /srv/jekyll

  • I’m using the jekyll-admin plugin. I was using the gem for it, with bundle. Now that jekyll runs in a container, I can install the gem into the container’s site-ruby. Create the file ‘Dockerfile’ in kleinfelter.github.io:

FROM jekyll/jekyll:pages
RUN gem install jekyll-admin
  • That says:

    • Base your image on the official jekyll plugin, the version designed to work with github-pages.

    • When building your image, run the given ‘gem’ command to add the gem.

  • Test it with:

docker-compose build --no-cache
docker-compose up --force-recreate
  • Edit runme-local.sh to contain:
#!/bin/bash
cd /Users/kevin/Sync/Sites/kleinfelter.github.io
/usr/local/bin/docker-compose up
  • Once you’re done testing: launchctl load /Users/kevin/Library/LaunchAgents/com.kleinfelter.jekyll.kleinfelter.plist

  • Occasionally, when Jekyll is running, I need to force a site rebuild.

    • Discover the proper container via: docker ps –format ‘table ‘

    • Connect to the existing container: docker exec -it CONTAINER_NAME_HERE /bin/bash

    • Run: jekyll build