C# Development in Emacs on Android

Why Android

I wanted to be able to do simple, multi-file development on my phone. The reason why was to do some hobby programming on my phone when I don’t have a PC. After noticing that there are not any full C# IDEs on Android, I thought about Emacs in Termux.

Termux has several limitations and no Mono Port, but I found I could run Mono under Arch Linux in Termux. With this, one can run Omnisharp and get auto-completion.

One thing I like about Termux is that it installs from the app store and doesn’t require rooting the device.

To do this, you will need about 3-4GB free on your primary storage on your Android device. Otherwise, you will run out of space installing all of the required components.

Use Arch Linux on Termux

The most up to date instructions on installing Arch on Termux are here:

https://wiki.termux.com/wiki/Arch

To repeat the page here, type the following:

pkg install wget
wget https://raw.githubusercontent.com/sdrausty/TermuxArch/master/setupTermuxArch.sh
bash setupTermuxArch.sh

This will install Arch Linux on Termux. It runs under proot (think of as pretend root+chroot). This causes a few issues with some functions, including some Emacs functions. I’ll work through those issues later.

When you launch Termux, go into Arch Linux as follows:

startarch

Note that Arch Linux takes over 1GB of storage.

Install Emacs and Mono

Once you are in Arch, install Emacs through the pacman repos:

pacman -S emacs

Now that you are running under Arch, you can install Mono:

pacman -S mono

Install Nuget

I went to the NuGet download page and got the command line version from Microsoft:

https://www.nuget.org/downloads

You can always try this as well:

wget https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
mv nuget.exe /bin/nuget.exe

I then created a shell script to run Nuget called nuget in /bin:

#!/bin/bash
exec mono /bin/nuget.exe $@

Remember to make nuget executable (chmod a+x nuget). With that, you can just type nuget commands at the shell as you would in Windows:

nuget sources

Install Omnisharp

OmniSharp will give you code completion, which makes C# development much easier.

Installation outlined on the OmniSharp page does not work. This has to do with set-file-modes (i.e. chmod) in Emacs not working. Also, downloading from the web does not work either. To work around the chmod issue, run the following elisp first (type in *scratch* and end with C-j):

(defun set-file-modes (one two) nil)

Because downloading from web sites doesn’t work, this breaks the OmniSharp Server install, so you have to install it manually.

Installation is outlined here https://github.com/OmniSharp/omnisharp-emacs, but below are the steps I took.

MELPA Setup

First set up MELPA https://github.com/melpa/melpa#usage by adding this to your .emacs or init.el:

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

Omnisharp Setup

Re-start Emacs and then you can install OmniSharp-Emacs as follows:

M-x package-refresh-contents RET
M-x package-install RET omnisharp RET

Now that all of this is set up, it won’t work until the server is installed.

Manually install OmniSharp Server

There is a page in the OmniSharp project that explains how to install the server. https://github.com/OmniSharp/omnisharp-emacs/blob/master/doc/server-installation.md

When I went to install the server, I had a few issues. Below is the method I used to install the server.

First, go to this page and get the link to the mono server (NOT http) and download it. https://github.com/OmniSharp/omnisharp-roslyn/releases Make sure to use omnisharp-mono.zip. Once you have downloaded the file, unzip it:

mkdir /usr/share/omnisharp-roslyn-server
unzip omnisharp-mono.zip -d /usr/share/omnisharp-roslyn-server

Create a shell script to run the server, OmniSharp.sh in /usr/share/omnisharp-roslyn-server:

#!/bin/bash
exec mono /usr/share/omnisharp-roslyn-server/OmniSharp.exe $@

Set Up Your init.el or .emacs

Set up autocomplete as you like. I didn’t want to use the methods listed (Company mode or Flycheck), so below is how I set it up using the default installed auto-complete functionality in Emacs:

(use-package omnisharp)
(add-hook 'csharp-mode-hook 'omnisharp-mode)
(setq omnisharp-server-executable-path "/usr/share/omnisharp-roslyn-server/OmniSharp.sh")
(define-key omnisharp-mode-map (kbd ".") 'omnisharp-add-dot-and-auto-complete)
(define-key omnisharp-mode-map (kbd "<C-SPC>") 'omnisharp-auto-complete)

Working With Csproj Files

You may want to work with csproj files. For this, there is a csproj mode that contains snippets for completing the csproj XML elements. Csproj mode looks like a work in progress, but it has some usefullness. https://github.com/omajid/csproj-mode

Download the mode using git (make sure to install git with pacman -S git). Copy or link the snippets under your .emacs.d folder.

 mkdir ~/src
 cd ~/src
 git clone https://github.com/omajid/csproj-mode
 mkdir ~/.emacs.d/snippets
 ln -s ~/src/csproj-mode/snippets/csproj-mode ~/.emacs.d/snippets/csproj-mode

Install the yasnippet minor mode and csproj-mode in Emacs:

M-x package-install RET yasnippet
M-x package-install-file RET ~/src/csproj-mode/csproj-mode.el

Finally, add a hooks for the csproj mode to automatically load the snippets and yas-minor-mode (if not using yas-global-mode):

(add-hook 'csproj-mode-hook 'yas-minor-mode)
(add-hook 'yas-minor-mode-hook 'yas-reload-all)

For more information on using csproj, see the MSBuild documentation. https://docs.microsoft.com/en-us/aspnet/web-forms/overview/deployment/web-deployment-in-the-enterprise/understanding-the-project-file This is useful for those of us used to the IDE managing the sln/csproj files for us.

Below is the build file for the program below:

<Project ToolsVersion="4.0" DefaultTargets="build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="build">
    <ItemGroup>
      <Compile Include="*.cs" />
    </ItemGroup>
    <CSC Sources="@(Compile)" 
         OutputAssembly="HelloWorld.exe" 
         EmitDebugInformation="true" />
  </Target>
</Project>

Building C# Programs

Let’s build a simple Hello World object oriented C# project. I recommend typing this in in C# mode with the Omnisharp server started to see what the experience is like.

To start the OmniSharp server in Emacs:

M-x omnisharp-start-omnisharp-server RET

Start with a string emitter interface. This way we change where the string is output later:

public interface IEmitter
{ void Emit(string message); }

And a console emitter that will output to the console. Other implementations could write anywhere else (browser, image, etc.):

public class ConsoleEmitter : IEmitter
{
  public void Emit(string message)
  { System.Console.Out.WriteLine(message); }
}

A message writer class that uses constructor dependency injection to get the class to write to the output:

public class MessageWriter {
  public string Message { get; set; }
  public IEmitter Emitter { get; set; }

  public MessageWriter(string message, IEmitter emitter)
  { Message = message; Emitter = emitter; }

  public void Emit() { Emitter.Emit(Message); }
}

A main method to pull it all together:

public class HelloApp
{
  public static void Main(string[] argv)
  {
    IEmitter con = new ConsoleEmitter();
    MessageWriter w = new MessageWriter("Hello World", con);
    w.Emit();
  }
}

Compile the app:

$ xbuild

Run the app:

$ mono HelloWorld.exe

Where Are We Now

At this point you can work with C# and csproj files in Emacs and get autocomplete with C#. You can also get help building csproj files using snippets.

With this, all you can do is build command prompt or simple .NET executables and libraries, but you can do multi-file projects with build.

Other Articles on C# and Emacs

Here are the other articles that helped my Droid/C#/Emacs journey: