Skip to content

jingtaozf/literate-smalltalk

Repository files navigation

Table of Contents

Introduction

literate-smalltalk is an Emacs lisp library and a Smalltalk library to provide an easy way to use literal programming in Smalltalk.

Pharo is a pure object-oriented programming language and a powerful environment, focused on simplicity and immediate feedback. It also provides a markdown syntax MicroDown for its class comment.

From the point of my view, Pharo is a wonderful IDE for development, except its native editor.

So here is a new literate programming environment for Pharo in Emacs org mode.

It setup a HTTP server in Pharo, so Emacs can interact with Pharo to

  • fetch packages, classes, method source
  • compile class/methods
  • format code block
  • show suggestions, etc.

In Emacs, we use an org file as both documentation and codes, and Emacs lisp library polymode to enable native source code block in org mode. In each code block, you can compile, format code, ask for suggestions, show compiling critiques instantly, just like it is inside Pharo.

This library contains the following files:

Currently it only tests in Pharo but other Smalltalk dialects should be easy to adopt.

Install

in Pharo

You can add this package via metacello by adding a file named as literate-server.st in Pharo’s configuration directory, For Pharo 9.0 it is in ~/.config/Pharo/9.0 in Linux, or ~/Library/Preferences/pharo in Mac OS. To know the Pharo’s configuration directory, please print the result of code StartupPreferencesLoader preferencesGeneralFolder in Pharo. the file content can be like this:

StartupPreferencesLoader default executeAtomicItems: { (StartupAction
			 name: 'Start Literate Server'
			 code: [
				 | class |
				 class := Smalltalk at: #LiterateServer ifAbsent: [
					          Metacello new
						          baseline: 'LiterateSmalltalk';
						          repository: 'github://jingtaozf/literate-smalltalk';
						          onConflict: [ :ex | ex allow ];
						          load.
					          Smalltalk at: #LiterateServer ].
				 class ifNotNil: [ LiterateServer start ] ]
			 runOnce: false) }

So each time Pharo starts, it will start a HTTP server listening in local port 9092, which Emacs can interact with.

Of course, you can also add it via Iceberg manually.

in Emacs

In Emacs side, you should install Emacs library literate-elisp firstly, then load this library in Emacs like this:

(load "~/projects/literate-elisp/literate-elisp.el")
(literate-elisp-load "~/projects/literate-smalltalk/literate-smalltalk.org")
(add-to-list 'org-src-lang-modes '("smalltalk" . "literate-smalltalk-code"))
(use-package poly-org :ensure t)

literate-smalltalk provides a new major mode literate-smalltalk-code for Smalltalk source file, we also ensure polymode mode use it.

Tutorial

I’ll show the general workflow and features of literate-smalltalk in this tutorial.

Let’s assume that you or your team have already created a git repository and imported the codes into Pharo, then you setup literate-smalltalk correctly so Pharo listens on port 9092 to wait for request from Emacs side.

Preparing an org file

let’s create an org file, that’s all for this step but I suggest the following lines in the beginning of an org file. You can check the raw content of ./literate-smalltalk.org to have a quick view.

  • enable poly-org mode
# -*- encoding:utf-8 Mode: POLY-ORG; tab-width: 2; org-src-preserve-indentation: t; -*- ---
  • remove the result part of all code block
#+PROPERTY: header-args :results silent
  • some default org properties for literate-smalltalk
#+PROPERTY: literate-load yes
#+PROPERTY: literate-lang smalltalk

A quick references to useful commands

'(
  ("package of class" literate-smalltalk-namespace-of-current-symbol)
  ("bindings of evaluation" literate-smalltalk-eval-bindings)
  ("c open definition of class" literate-smalltalk-browse-class)
  ("Compile codes in current header" literate-smalltalk-execute-current-header)
  ("execute codes" literate-smalltalk-execute-current-code-block)
  ("delete current class method" literate-smalltalk-delete-current-class-or-method)
  ("format code for current code block" literate-smalltalk-code-format-current-code-block)
  ("Format code for current file" literate-smalltalk-code-format-current-file)
  ("i open definition of implementors" literate-smalltalk-browse-implementors)
  ("run current line or selected region" literate-smalltalk-eval-current-line-or-selected-region)
  ("update codes" literate-smalltalk-update-source))

import source codes of some packages

Generally speaking, the first step is importing some Smalltalk packages into our org file.

We provide two Emacs command for this purpose:

Now you have some source codes inside your org file.

compile a code block

A code block can contain either a class definition or a method code, you can execute each source code block by Emacs command literate-smalltalk-execute-current-code-block, or execute in org mode by org-babel-execute-src-block-maybe.

After compiling, it will show critiques by adding them as Overlays.

Please note that we use the following codes for a class definition in a code block

Object subclass: #LiterateServer
    instanceVariableNames: ''
    classVariableNames: 'Server Started interactionModel transcriptLogStream'
    package: 'LiterateSmalltalk'.
LiterateServer class
    instanceVariableNames: ''.
LiterateServer comment: 'The REST Server for LiterateSmalltalk.'

format code block

It is better to format code before compiling, you can do so by Emacs command literate-smalltalk-code-format-current-code-block.

show suggestions

We use company mode to show suggestions, via Emacs command company-literate-smalltalk-code. You can press shortcut key Alt-/ or Tab to show a suggestion menu.

delete current class or method

You can delete it in current code block by Emacs command literate-smalltalk-delete-current-class-or-method.

update source code block from Pharo

Sometimes you change some code inside Pharo, to get the latest code, you can update current code block by Emacs command literate-smalltalk-update-source.

eval code (REPL)

You can create a code block with additional header argument :type code, in this case when you compile this code block, it is evaluated, and if you created a variable in it, you can use this variable in another code block with header argument :type code.

For me, I will create an individual org file for one project as an REPL for it.

# -*- Mode: POLY-ORG; encoding: utf-8; tab-width: 2;  -*- ---
#+Title: The REPL of literate-smalltalk
#+OPTIONS: tex:t toc:2 \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
#+STARTUP: noindent
#+STARTUP: inlineimages
#+PROPERTY: literate-header-arguments :type code
#+PROPERTY: literate-lang smalltalk
#+PROPERTY: literate-load yes

To just eval current line or selected region, you can run command literate-smalltalk-eval-current-line-or-selected-region.

browse class or implementors in Pharo

The Emacs command To browse class in Pharo Window is literate-smalltalk-browse-class. The Emacs command To browse implementors in Pharo Window is literate-smalltalk-browse-implementors.

compile all source code blocks inside a section

To compile all source code blocks inside a section, please invoke the Emacs command literate-smalltalk-execute-current-header. It will compiling all code blocks from current point to the end of current section.

If you execute this command with command prefix C-u, it will execute all code blocks from current point to the end of current buffer.

release current package to local file system

I release codes of this project to local file system by method releaseIcebergPackage in class LiterateServer.

LiterateServer releaseIcebergPackage: #LiterateSmalltalk.

I find it useful because Iceberg will have detached working copy sometimes.

References