Categories
Software Engineering

Tricks I needed to install Fedora 26 on a Dell XPS 13 9365 (2 in 1)

Update: I ended up getting a new job which came with a new laptop, so don’t have the XPS 9365 anymore. I hope this post is still helpful to people but I won’t be able to provide any more support. The official Fedora support page is over here: http://ask.fedoraproject.org/ Good luck everyone!


While having breakfast on Friday morning, my 5 year old laptop was going fine.  Then Firefox froze.  I pressed alt-tab, nope, everything is frozen except the mouse. Then the mouse was frozen.  Then I reset the computer, and got this message “Operating System Not Found”.  My hard drive had died.

Rather than spend a weekend fiddling to repair it, I decided to spend my tax-return money to buy a new laptop – a Dell XPS 13 9365 2-in-1.  Fancy as!  But, whenever you buy a fairly knew and fancy laptop, less than 12 months old, with the intent to install Linux – you should probably set aside some time because you just know there’s going to be issues.

One weekend later, I’m the happy owner of a XPS 13 2-in-1 running Fedora 26.  Here’s all the tips and gotchas and cry-into-a-pillow moments that I had to get through to make it this far.

Trying Fedora instead of Ubuntu

Before I made the purchase, I was doing some Googling to see if Ubuntu would even load on an XPS 13 9365.  The verdict seemed to be that it would load, but there was some difficulty getting suspend/resume to work, but it was possible.  I decided to go ahead with the purchase. But in my reading, I came across this comment:

I was unable to uninstall Ubuntu on the XPS at all. And out of frustration I tried Fedora and I was simply BLOWN away by the polish. And today we have Fedora 26 that is even better. I am semi-validated by Ubuntu moving to Gnome as well. Ubuntu was simply too unpolished with Mir + Unity.

I decided to give Fedora a go.  Now that most of my development work happens in Docker, I’m not too worried about which distro I have running on bare-metal – and I’m up for trying something new!

Verdict: I’ve enjoyed Fedora – the polish in Fedora 26 really is there compared to Ubuntu 16.04 (admittedly – it is 12 months newer so that is to be expected).

To get started with Fedora, download the “Fedora Media Writer” which will prepare a Live USB for you. See the Fedora installation guide for more info.

Shrinking a Windows Partition is beyond my pay-grade

At first I was interested in keeping Windows 10 installed and dual booting, because it might be nice to occasionally see test how it works etc.  But part of the dual-boot process involves resizing the Windows partition to make space for Linux.

I had a 460GB Windows partition, with 30GB used.  For the life of me I couldn’t shrink it smaller than 445GB – leaving only 15GB for Linux.  I tried following different tips, tricks and tutorials for about 30 minutes, and then decided that I’ve lived without Windows for a decade, I can keep going without it now.

SATA mode has to be AHCI

By default the 9365 has it’s SATA hard drive configured to be in “RAID” mode rather than “AHCI”.  To be able to install Fedora, I needed to change this to AHCI. Not sure why.  Here’s a question / answer that prompted me to make the change.

It’s worth noting that if you intend to dual boot, changing from “RAID” to “AHCI” can cause serious problems for Windows unless you do some prep work first.  You can change it and change back, but if you want to dual boot, you will need both to be on AHCI.

A painful firmware bug (that makes you think your laptop is dead forever)

See: http://en.community.dell.com/support-forums/laptop/f/3518/t/20009547

This bug had me thinking my laptop was bricked and would need to be sent for warranty.  It would literally sit on the DELL logo for what felt like forever, but turned out to be 5 to 10 minutes.  I can’t explain how relieved I was to read a blog post where someone described the same symptoms:

When changing the SATA drive setting from RAID to AHCI, and disabling the “Secure boot” option in the BIOS (both actions are needed to install Ubuntu), the booting process gets stuck in the Dell logo for a long time, around 5 minutes, before it makes any progress. Even trying to enter the BIOS again to change those settings makes me have to wait that long.

Also, when booting when those settings on and entering the BIOS, the whole user interface of the BIOS menu, even just moving the mouse cursor around, is extremely slow. Clicking on a menu option on the BIOS makes the screen refresh to the next screen with a very slow transition of about 3 seconds.

I’m have upgraded to the latest BIOS firmware as of April 8, 2017 (Version 01.00.10, 3/9/2017). This bug is currently preventing me from setting up a dual-boot mode with Windows 10 + Ubuntu, which makes the system not usable for my specific use cases. I’d really appreciate if these issues could be resolved soon.

The fix:

  • You can’t have “SATA MODE = AHCI” and “SECURE BOOT = FALSE” at the same time.
  • Because “SATA MODE = AHCI” is required for a Fedora install, we need “SECURE BOOT’ to be true.  Turns out, this is actually okay.

That one hurt.  I also found a poor user on Reddit who was bit by the same problem and was offering money to anyone who could help – too bad I didn’t find the fix 2 months earlier!

Configuring BIOS to boot from USB

One final thing to do in BIOS: configure it to boot from USB.

Because we’re using SecureBoot, this is not as straight forwarded as choosing an option from a boot menu.

Steps:

  • Ensure “Disable Legacy Boot ROMs” is ticked.  It will need to be ticked for secure-boot to be ticked.
  • Ensure “Secure Boot” is ticked.  It’s on a different page in the settings.
  • Ensure “Boot mode” is “UEFI” not “Legacy”.
  • This will show a list of boot options.  The terrible GUI interface will require you to scroll down to find the “Add Boot Option” button. Click it.
  • Add a boot option named “Fedora” and click the “…” to open the file browser.
  • Find your USB drive in the list (mine was named “Anaconda” by the Fedora Media Writer).
  • Load the file “/EFI/BOOT/grubx64.efi
  • Save the new boot item.  Use drag and drop to move it to the top, so it has the highest priority.
  • Save your settings, and restart – and hopefully – Fedora will load up and kick into Live CD mode.

Before you install

Before I hit install, I did a quick check:

  • Wifi works: yes
  • Sound works: yes
  • Touchscreen works: yes
  • Webcam works: yes
  • Suspend / Resume works: no. Bummer – but my research had suggested this was probably going to be an issue, so I continued anyway.

In the install options I deleted the Windows 10 partition, and got it to auto-partition from there.  Then hit install.  Woo!

Getting suspend and resume to work.

**Update:** It turns out I’m still having supend/resume issues.  I think figuring out how to install the 4.13 version of the kernel while SecureBoot is enabled is what I will need.

After the install, almost everything worked as expected, and the whole experience was really nice – it’s a beautiful laptop, and the new version of Fedora with Gnome 3 is quite pleasant.  Until you close the lid and it suspends.  Because then it won’t wake up again.

What would happen:

  • The screen would go dark, but the keyboard backlight would stay on.
  • Pressing the tiny power button on the side of the case does nothing at first.
  • If you keep holding the power button for like 10 seconds, the login screen lights up, and everything is still there, but the moment you let go, it suspends again.
  • If you hold it down long enough, it eventually turns off.  You’ll need to do this to get out of the broken suspend, but it takes forever and feels like you’re pressing the little power button so hard you’ll break it.

What I learned about why it doesn’t work:

  • It’s a Linux kernel issue.  See this bug report.
  • You can check your kernel version by typing
    [jason@jasonxps enthraler]$ uname -a
    Linux jasonxps 4.11.8-300.fc26.x86_64 #1 SMP .....
  • I read a bunch of Q&A suggestions on tips for getting this to work, but none helped that much – reading through the bug report above though convinced me that I needed to upgrade from 4.11 to 4.12 or 4.13.
  • Upgrading to the very latest kernel (4.13-rc4) seems easy, but as the wiki page notes, it won’t work with SecureBoot – so that turned out to be a dead end for me.  (Signing the kernel for SecureBoot might be possible, but I couldn’t be bothered learning enough to understand the tutorials).
  • 4.12 isn’t released yet, but it’s supposed to be in testing.  Unfortunately, enabling the “updates-testing” repository and running “dnf upgrade” didn’t install the new kernel.  I’m not sure if it was supposed to.
  • In the end, I installed 3 RPMs manually.  (GASP!)
  • Here is the page with the packages I need.  Apparently it’s only been in testing for 9hours – perhaps that is why it wasn’t coming through the update channel?
  • I had to download these 3 files:
  • Once downloaded, I ran this to install them:
    dnf install kernel-modules-4.12.5-300.fc26.x86_64.rpm kernel-core-4.12.5-300.fc26.x86_64.rpm kernel-4.12.5-300.fc26.x86_64.rpm

    Be careful here that you don’t override the kernel you’re currently using.  You may need to add options to “dnf” if it suggests that it’s going to remove the package for the kernel you’re currently on.

  • And then you update grub:
    sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
  • After restarting, test that the new kernel is working:
    [jason@jasonxps enthraler]$ uname -a
    Linux jasonxps 4.12.5-300.fc26.x86_64 #1 SMP ...
  • And now, you can close the lid and expect it to suspend and resume.  For me, I still have to hold the power button for like 6 seconds to get it to resume, but hey, at least it comes back.  I’m hoping 4.13 will come out and fix that problem too.
  • Note – I also changed the setting in Gnome Power Settings for “When the Power Button is Pressed” from “Suspend” to “Nothing”.  Reason: sometimes holding down the power button that long to resume it would then trigger another “supsend”.  So I set the button to do nothing.  I can just close the lid to supsend.

Ubuntu Unity-like keyboard shortcuts

Overall I’ve really enjoyed Gnome 3 over Ubuntu Unity.  One thing I missed though was being able to press “Win+1” to open my file manager, “Win+2” to open Firefox, “Win+3” to open Visual Studio Code, “Win+4” to open Chrome etc. Basically, my most common applications all sit in the dock on the left, and I can use a quick keyboard shortcut to switch to that app – and if it’s not open already, it will open it.  Gnome doesn’t have this by default.

Luckily, there’s an extension that adds this behaviour: https://github.com/franziskuskiefer/app-keys-gnome-shell-extension

Conclusion

Well, that was certainly not something I’d trust my Grandma to complete successfully.  But hey – at least it works.  If I learn any new tricks for getting Fedora to run nicely on a Dell XPS13 9365 2-in-1, I’ll post here.

If you have any questions, feel free to ask – no guarantees I’ll be able to help though :)

 

Categories
Haxe

Creating Complex URL Routing Schemes With haxe.web.Dispatch

Complex Routing Schemes with haxe.web.Dispatch

I’ve looked at haxe.web.Dispatch before, and I thought it looked really simple – in both a good and a bad way. It was easy to set up and fast. But at first it looked like you couldn’t do complex URL schemes with it.

Well, I was wrong.

At the WWX conference I presented some of the work I’ve done on Ufront, a web application framework built on top of Haxe. And I mentioned that I wanted to write a new Routing system for Ufront. Now, Ufront until now has had a pretty powerful routing system, but it was incredibly obese – using dozens of classes, thousands of lines of code and a lot of runtime type checking, which is usually pretty slow.

Dispatch on the other hand uses macros for a lot of the type checking, so it’s much faster, and it also weighs in at less than 500 lines of code (including the macros!), so it’s much easier to comprehend and maintain. To wrap my head around the ufront framework took me most of the day, following the code down the rabbit hole, but I was able to understand the entire Dispatch class in probably less than an hour. So that is good!

But there is still the question, is it versatile enough to handle complex URL routing schemes? After spending more time with it, and with the documentation, I found that it is a lot more flexible than I originally thought, and should work for the vast majority of use cases.

Learning by example

Take a look at the documentation to get an idea of general usage. There’s some stuff in there that I won’t cover here, so it’s well worht a read. Once you’ve got that understood, I will show how your API / Routing class might be structured to get various URL schemes:

If we want a default homepage:

function doDefault() trace ( "Welcome!" ); 

If we want to do a single page /about/:

function doAbout() trace ( "About us" ); 

If you would like to have an alias route, so a different URL that does the same thing:

inline function doAboutus() doAbout();

If we want a page with an argument in the route /help/{topic}/:

function doHelp( topic:String ) { 
 trace ( 'Info about $topic' ); 
} 

It’s worth noting here that if topic is not supplied, an empty string is used. So you can check for this:

function doHelp( topic:String ) { 
 if ( topic == "" ) { 
 trace ( 'Please select a help topic' ); 
 } 
 else { 
 trace ( 'Info about $topic' ); 
 } 
} 

Now, most projects get big enough that you might want more than one API project, and more than one level of routes. Let’s say we’re working on the haxelib website (a task I might take on soon), there are several pages to do with Projects (or haxelibs), so we might put them altogether in ProjectController, and we might want the routing to be available in /projects/{something}/.

If we want a sub-controller we use it like so:

/*
 /projects/ => projectController.doDefault("")
 /projects/{name}/ => projectController.doDefault(name)
 /projects/popular/ => projectController.doPopular
 /projects/bytag/{tag}/ => projectController.doByTag(tag) 
*/ 
function doProjects( d:Dispatch ) { 
 d.dispatch( new ProjectController() ); 
} 

As you can see, that gives a fairly easy way to organise both your code and your URLs: all the code goes in ProjectController and we access it from /projects/. Simple.

With that example however, all the variable capturing is at the end. Sometimes your URL routing scheme would make more sense if you were to capture a variable in the middle. Perhaps you would like:

/users/{username}/ 
/users/{username}/contact/ 
/users/{username}/{project}/ 

Even this can still be done with Dispatch (I told you it was flexible):

function doUsers( d:Dispatch, username:String ) { 
 if ( username == "" ) { 
 println("List of users"); 
 } 
 else { 
 d.dispatch( new UserController(username) ); 
 } 
} 

And then in your username class:

class UserController { 
 var username:String; 
 public function new( username:String ) { 
 this.username = username; 
 } 
 function doDefault( project:String ) { 
 if ( project == "") { 
 println('$username\'s projects'); 
 } 
 else { 
 println('$username\'s $project project'); 
 } 
 } 
 function doContact() { 
 println('Contact $username'); 
 } 
} 

So the username is captured in doUsers(), and then is passed on to the UserController, where it is available for all the possible controller actions. Nice!

As you can see, this better represents the heirarchy of your site – both your URL and your code are well structured.

Sometimes, it’s nice to give users a top-level directory. Github does this:

http://github.com/jasononeil/

We can too. The trick is to put it in your doDefault action:

function doDefault( d:Dispatch, username:String ) {
  if ( username == "" ) { 
 println("Welcome"); 
 } 
 else { 
 d.dispatch( new UserController(username) ); 
 } 
} 

Now we can do things like:

/ => doDefault() 
/jason/ => UserController("jason").doDefault("") 
/jason/detox => UserController("jason").doDefault("detox") 
/jason/contact => UserController("jason").doContact() 
/projects/ => ProjectController.doDefault("") 
/about/ => doAbout() 

You can see here that Dispatch is clever enough to know what is a special route, like “about” or “projects/something”, and what is a username to be passed on to the UserController.

Finally, it might be the case that you want to keep your code in a separate controller, but you want to have it stay in the top level routing namespace.

To use a sub-controller add methods to your Routes class:

inline function doSignUp() (new AuthController()).doSignUp(); 
inline function doSignIn() (new AuthController()).doSignIn(); 
inline function doSignOut() (new AuthController()).doSignOut(); 

This will let the route be defined at the top level, so you can use /signup/ rather than /auth/signup/. But you can still keep your code clean and separate. Winning. The inline will also help minimise any performance penalty for doing things this way.

Comparison to Ufront/MVC style routing

Dispatch is great. It’s light weight so it’s really fast, and will be easier to extend or modify the code base, because it’s easier to understand. And it’s flexible enough to cover all the common use cases I could think of.

I’m sure if I twisted my logic far enough, I could come up with a situation that doesn’t fit, but for me this is a pretty good start.

There are three things the old Ufront routing could do that this can’t:

  1. Filtering based on whether the request is Post / Get etc. It was possible to do some other filtering also, such as checking authentication. I think all of this can be better achieved with macros rather than the runtime solution in the existing ufront routing framework.
  2. LocalizedRoutes, but I’m sure with some more macro love we could make even that work. Besides, I’m not sure that localized routes ever functioned properly in Ufront anyway ;)
  3. Being able to capture “controller” and “action” as variables, so that you can set up an automatic /{controller}/{action}/{...} routing system. While this does make for a deceptively simple setup, it is in fact quite complex behind the scenes, and not very type-safe. I think the “Haxe way” is to make sure the compiler knows what’s going on, so any potential errors are captured at compile time, not left for a user to discover.

Future

I’ll definitely begin using this for my projects, and if I can convince Franco, I will make it the default for new Ufront projects. The old framework can be left in there in case people still want it, and so that we don’t break legacy code.

I’d like to implement a few macros to make things easier too.

So

var doUsers:UserController; 

Would automatically become:

function doUsers( ?d:Dispatch, username:String ) { 
 d.dispatch( new UserController(username) ); 
} 

The arguments for “doUsers()” would match the arguments in the constructor of “UserController”. If “username” was blank, it would still be passed to UserController.default, which could test for a blank username and take the appropriate action.

I’d also like:

@:forward(doSignup, doSignin, doSignout) var doAuth:AuthController; 

To become:

function doAuth( ?d:Dispatch ) { 
 d.dispatch( new AuthController() ); 
} 
inline function doSignup() (new AuthController()).doSignup(); 
inline function doSignin() (new AuthController()).doSignin(); 
inline function doSignout() (new AuthController()).doSignout(); 

Finally, it would be nice to have the ability to do different actions depending on the method. So something like:

@:method(GET) function doLogin() trace ("Please log in");

@:method(POST) function doLogin( args:{ user:String, pass:String } ) { 
 if ( attemptLogin(user,pass) ) { 
 trace ( 'Hello $user' ); 
 } 
 else { 
 trace ( 'Wrong password' ); 
 } 
}
 
function doLogin() { 
 trace ( "Why are you using a weird HTTP method?" ); 
} 

This would compile to code:

function get_doLogin() { 
 trace ("Please log in") 
} 

function post_doLogin( args:{ user:String, pass:String } ) { 
 if ( attemptLogin(user,pass) ) { 
 trace ( 'Hello $user' ); 
 } 
 else { 
 trace ( 'Wrong password' ); 
 } 
} 

function doLogin() { 
 trace ( "Why are you using a weird HTTP method?" ) 
} 

Then, if you do a GET request, it first attempts “get_doLogin”. If that fails, it tries “doLogin”.

The aim then, would be to write code as simple as:

class Routes { 
 function doDefault() trace ("Welcome!"); 
 var users:UserController; 
 var projects:ProjectController; 
 @:forward(doSignup, doSignin, doSignout) var doAuth:AuthController; 
} 

So that you have a very simple, readable structure that shows how the routing flows through your apps.

When I get these macros done I will include them in ufront for sure.

Example Code

I have a project which works with all of these examples, in case you have trouble seeing how it fits together.

It is on this gist.

In it, I don’t use real HTTP paths, I just loop through a bunch of test paths to check that everything is working properly. Which it is… Hooray for Haxe!

Categories
Haxe

The new Map syntax in Haxe 3

I was using the Haxe3 Release Candidate for well over a month before I realised that the new Map data structures could be created with a nice syntax.  The basic gist is this:

var map1 = [ 1=>"one", 2=>"two", 3=>"three" ];

Like Arrays, but you use the “=>” operator to define both the key and the value.  And Haxe’s type inference is as strong as ever.  Here’s what the compiler picks up:

var map1 = [ 1=>"one", 2=>"two", 3=>"three" ];
$type (map1); // Map<Int, String>

var map2 = [ "one"=>1, "two"=>2, "three"=>3 ];
$type (map2); // Map<String, Int>

var map3 = [
    Date.now() => "Today",
    Date.now().delta(24*60*60*1000) => "Tomorrow",
    Date.now().delta(-24*60*60*1000) => "Yesterday"
];
$type (map3); // Map<Date, String>

var map4 = [
    { name: "Tony Stark" } => "Iron Man",
    { name: "Peter Parker" } => "Spider Man"
];
$type (map4); // Map<{ name : String }, String>

var map5 = [
    [1,2] => ["one","two"],
    [3,4] => ["three","four"],
];
$type (map5); // Map<Array<Int>, Array<String>>

So it’s pretty clever.  If for some reason you need to type explicitly to StringMap, IntMap, or ObjectMap, you can:

var stringMap:StringMap<Int> = [
    "One" => 1,
    "Two" => 2
];
$type(stringMap); // haxe.ds.StringMap<Int>

var intMap:IntMap<String> = [
    1 => "One",
    2 => "Two"
];
$type(intMap); // haxe.ds.IntMap<String>

var objectMap:ObjectMap<{ name:String }, String> = [
    { name: "Tony Stark" } => "Iron Man",
    { name: "Peter Parker" } => "Spider Man"
];
$type(objectMap); // haxe.ds.ObjectMap<{ name : String }, String>

But if you try to do anything too funky with types, the compiler will complain.  Haxe likes to keep things strictly typed:

var funkyMap = [
    { name: "Tony Stark" } => "Iron Man",
    { value: "Age" } => 25
]; // Error: { value : String } has no field name ... 
   // you are a bad person, and your items are not comprehensible 
   // to Haxe's typing system

Finally, Haxe won’t let you do define duplicate keys using this syntax:

var mapWithDuplicates = [
    1 => "One",
    2 => "Two",
    1 => "uno"
]; // Error: Duplicate Key ... previously defined (somewhere)

If you’re using an object map, it’s only a duplicate if you’re dealing with the exact same object, not a similar one.  For example, this is allowed:

var similarObjectKeys:ObjectMap<Array<Int>, String> = [
    [0] => "First Array object",
    [0] => "Second Array object"
]; // Works, you now have 2 items in your map.

But if you use the exact same object, Haxe will pick it up:

var key = [0];
var sameObjectKey = [
    key => "First Array object",
    key => "Second Array object"
]; // Error: Duplicate Key ...

So there you have it.  A nice feature that I didn’t see mentioned anywhere else.  Thanks Haxe team!

….

Update:

It’s worth mentioning that once you have created your map, you can use array access (“[” and “]”) to read or modify entries in your map.

var map = [ 1=>"one", 2=>"two", 3=>"three" ];

// Reading a value
map[1];   "one"
var i = 2;
map[i];   "two"
map[++i]; "three"

// Setting a value
map[4] = "four";
map[1] = "uno";
map[i] = "THREE";
map;   // [ 1=>"uno", 2=>"two", 3=>"THREE", 4=>"four" ]
Categories
Haxe

Haxe3 Features: Variable Substitution in Haxe3 (aka String Interpolation)

One of my favourite features in the upcoming Haxe3 is also one of the simplest: String Interpolation (also called variable substitution).  If you were using Std.format() in Haxe2, you’ll recognise it.  It lets you do this:

var name = "Jason";
var age = 25;
trace ('My name is $name, I am $age years old.');
// My name is Jason, I am 25 years old

The first point to make is that it is triggered by using single quotation marks (‘), rather than double (“).  If you use double quotes, everything is treated as a plain string:

trace ("My name is $name, I am $age years old.");
// My name is $name, I am $age years old

The next point to make is that this all happens at compile time.  So the trace we made above, in Javascript, would output as:

console.log("My name is " + name + ", I am " + age + " years old");

So you can only do this with strings you have at compile time, as you’re writing the code.  If you want Strings that are given at runtime to have variable interpolation, you should use a templating library like erazor.

Now a few other things to note:

  1. If you want to put in a normal dollars sign, you can use two $, like this:
    trace ('The item costs $20');
    // "20" is not a valid variable name, so it is ignored.
    // Same as ("The " + item + " costs $20");
    
    trace ('That price is in $USD');
    // Error: Unknown Identifier "USD"
    trace ('That price is in $$USD');
    // Same as ("That price is in $" + "USD");
    
    var cost = 25;
    trace ('That item costs $$$cost');
    // The first two "$" are for the literal sign, the third is part of the variable.
    // Same as ("That item costs $" + cost);
  2. If you want to access anything more than a straight variable, use curly brackets:
    // Property access
    trace ('My name is ${user.name} and I am ${user.age} years old.');
    // Same as: "My name is " + user.name + " and I am " + user.age + " years old.";
    // Outputs: My name is jason and I am 25 years old.
    
    // A simple haxe expression
    trace ('$x + $y = ${x + y}');
    // Same as: "" + x + " + " + y + " = " + (x + y);
    // Outputs: 1 + 2 = 3
    
    // A function call 
    trace ('The closest Int to Pi is ${Math.round(3.14159)}');
    // Same as: "The closest Int to Pi is " + Math.round(3.14159);
    // Outputs: The closest Int to Pi is 3
  3. There is no HTML escaping, so be careful:
    var bold = "<b>Bold</b>";
    trace ('<i>Italic</i> $bold');
    // <i>Italic</i> <b>Bold</b>
    
    var safeBold = StringTools.htmlEscape("<b>Bold</b>");
    trace ('<i>Italic</i> $safeBold');
    // <i>Italic</i> &lt;b&gt;Bold&lt;/b&gt;
    
    var safeEverything = StringTools.htmlEscape('<i>Italic</i> $bold');
    trace (safeEverything);
    // &lt;i&gt;Italic&lt;/i&gt; &lt;b&gt;Bold&lt;/b&gt;

There you have it: String Interpolation, one of the most helpful (and easy to comprehend) features of the upcoming Haxe3 release.