diff --git a/ihaskell.cabal b/ihaskell.cabal
--- a/ihaskell.cabal
+++ b/ihaskell.cabal
@@ -7,7 +7,7 @@
 -- PVP summary:      +--+------- breaking API changes
 --                   |  | +----- non-breaking API additions
 --                   |  | | +--- code changes with no API change
-version:             0.11.0.0
+version:             0.12.0.0
 
 -- A short (one-line) description of the package.
 synopsis:            A Haskell backend kernel for the Jupyter project.
@@ -47,7 +47,6 @@
     html/logo-64x64.svg
     jupyterlab-ihaskell/labextension/package.json
     jupyterlab-ihaskell/labextension/static/*.js
-    jupyterlab-ihaskell/labextension/static/*.json
 
 flag use-hlint
   description: Include HLint support
@@ -72,7 +71,7 @@
                        bytestring            ,
                        exceptions            ,
                        filepath              ,
-                       ghc                   >=8.0 && <9.9,
+                       ghc                   >=8.4 && <9.11,
                        ghc-boot              ,
                        haskeline             ,
                        parsec                ,
diff --git a/jupyterlab-ihaskell/labextension/package.json b/jupyterlab-ihaskell/labextension/package.json
--- a/jupyterlab-ihaskell/labextension/package.json
+++ b/jupyterlab-ihaskell/labextension/package.json
@@ -29,25 +29,23 @@
     "watch": "tsc -w"
   },
   "dependencies": {
-    "@jupyterlab/application": ">=3.0.0",
-    "@jupyterlab/codemirror": ">=3.0.0",
-    "@jupyterlab/apputils": ">=3.0.0",
-    "@jupyterlab/docregistry": ">=3.0.0",
-    "@jupyterlab/notebook": ">=3.0.0",
-    "@jupyterlab/services": ">=6.0.0"
+    "@jupyterlab/application": ">=4.0.0",
+    "@jupyterlab/codemirror": ">=4.0.0",
+    "@jupyterlab/apputils": ">=4.0.0",
+    "@jupyterlab/notebook": ">=4.0.0"
   },
   "devDependencies": {
     "rimraf": "^3.0.0",
-    "typescript": "~3.7.3",
+    "typescript": "~5.0.4",
     "@types/codemirror": ">=0.0.0",
-    "@jupyterlab/builder": ">=3.0.0"
+    "@jupyterlab/builder": ">=4.0.0"
   },
   "jupyterlab": {
     "extension": true,
     "outputDir": "labextension",
     "webpackConfig": "./webpack.config.js",
     "_build": {
-      "load": "static/remoteEntry-ddc4c9d791a676e50bb9.js",
+      "load": "static/remoteEntry-33e79930ce2a88033aa3.js",
       "extension": "./extension"
     }
   }
diff --git a/jupyterlab-ihaskell/labextension/static/568-1b6fb876268b3f3be3f4.js b/jupyterlab-ihaskell/labextension/static/568-1b6fb876268b3f3be3f4.js
deleted file mode 100644
--- a/jupyterlab-ihaskell/labextension/static/568-1b6fb876268b3f3be3f4.js
+++ /dev/null
@@ -1,1 +0,0 @@
-"use strict";(self.webpackChunkjupyterlab_ihaskell=self.webpackChunkjupyterlab_ihaskell||[]).push([[568],{291:function(e,t,l){var i=this&&this.__awaiter||function(e,t,l,i){return new(l||(l=Promise))((function(n,o){function r(e){try{s(i.next(e))}catch(e){o(e)}}function a(e){try{s(i.throw(e))}catch(e){o(e)}}function s(e){var t;e.done?n(e.value):(t=e.value,t instanceof l?t:new l((function(e){e(t)}))).then(r,a)}s((i=i.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0});const n=l(36);t.defineIHaskellMode=function({CodeMirror:e}){return i(this,void 0,void 0,(function*(){yield n.Mode.ensure("haskell"),yield n.Mode.ensure("r"),e.defineMode("ihaskell",(t=>{let l=e.getMode(t,"haskell");return e.multiplexingMode(l,{open:/:(?=!)/,close:/^(?!!)/,mode:e.getMode(t,"text/plain"),delimStyle:"delimit"},{open:/\[r\||\[rprint\||\[rgraph\|/,close:/\|\]/,mode:e.getMode(t,"text/x-rsrc"),delimStyle:"delimit"})})),e.defineMIME("text/x-ihaskell","ihaskell"),e.modeInfo.push({ext:["hs"],mime:"text/x-ihaskell",mode:"ihaskell",name:"ihaskell"})}))}},568:(e,t,l)=>{Object.defineProperty(t,"__esModule",{value:!0});const i=l(36),n=l(291),o={id:"ihaskell",autoStart:!0,requires:[i.ICodeMirror],activate:(e,t)=>{n.defineIHaskellMode(t).catch(console.warn)}};t.default=o}}]);
diff --git a/jupyterlab-ihaskell/labextension/static/568-ddb8cadba5e09aa373e6.js b/jupyterlab-ihaskell/labextension/static/568-ddb8cadba5e09aa373e6.js
new file mode 100644
--- /dev/null
+++ b/jupyterlab-ihaskell/labextension/static/568-ddb8cadba5e09aa373e6.js
@@ -0,0 +1,1 @@
+"use strict";(self.webpackChunkjupyterlab_ihaskell=self.webpackChunkjupyterlab_ihaskell||[]).push([[568],{568:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0});const n=r(373),a=r(428),i=r(101),o={id:"ihaskell",autoStart:!0,description:"A CodeMirror extension for IHaskell",requires:[a.IEditorLanguageRegistry],activate:async(e,t)=>{const r=new n.LanguageSupport(n.StreamLanguage.define(i.haskell));t.addLanguage({name:"ihaskell",mime:"text/x-ihaskell",support:r,extensions:["hs"]})}};t.default=o},101:(e,t)=>{function r(e,t,r){return t(r),r(e,t)}Object.defineProperty(t,"__esModule",{value:!0});var n=/[a-z_]/,a=/[A-Z]/,i=/\d/,o=/[0-9A-Fa-f]/,l=/[0-7]/,u=/[a-z_A-Z0-9'\xa1-\uffff]/,s=/[-!#$%&*+.\/<=>?@\\^|~:]/,c=/[(),;[\]`{}]/,f=/[ \t\v\f]/;function d(e,t){if(e.eatWhile(f))return null;var d=e.next();if(c.test(d)){if("{"==d&&e.eat("-")){var p="comment";return e.eat("#")&&(p="meta"),r(e,t,h(p,1))}return null}if("'"==d)return e.eat("\\"),e.next(),e.eat("'")?"string":"error";if('"'==d)return r(e,t,m);if(a.test(d))return e.eatWhile(u),e.eat(".")?"qualifier":"type";if(n.test(d))return e.eatWhile(u),"variable";if(i.test(d)){if("0"==d){if(e.eat(/[xX]/))return e.eatWhile(o),"integer";if(e.eat(/[oO]/))return e.eatWhile(l),"number"}return e.eatWhile(i),p="number",e.match(/^\.\d+/)&&(p="number"),e.eat(/[eE]/)&&(p="number",e.eat(/[-+]/),e.eatWhile(i)),p}return"."==d&&e.eat(".")?"keyword":s.test(d)?"-"==d&&e.eat(/-/)&&(e.eatWhile(/-/),!e.eat(s))?(e.skipToEnd(),"comment"):(e.eatWhile(s),"variable"):"error"}function h(e,t){return 0==t?d:function(r,n){for(var a=t;!r.eol();){var i=r.next();if("{"==i&&r.eat("-"))++a;else if("-"==i&&r.eat("}")&&0==--a)return n(d),e}return n(h(e,a)),e}}function m(e,t){for(;!e.eol();){var r=e.next();if('"'==r)return t(d),"string";if("\\"==r){if(e.eol()||e.eat(f))return t(p),"string";e.eat("&")||e.next()}}return t(d),"error"}function p(e,t){return e.eat("\\")?r(e,t,m):(e.next(),t(d),"error")}var g=function(){var e={};function t(t){return function(){for(var r=0;r<arguments.length;r++)e[arguments[r]]=t}}return t("keyword")("case","class","data","default","deriving","do","else","foreign","if","import","in","infix","infixl","infixr","instance","let","module","newtype","of","then","type","where","_"),t("keyword")("..",":","::","=","\\","<-","->","@","~","=>"),t("builtin")("!!","$!","$","&&","+","++","-",".","/","/=","<","<*","<=","<$>","<*>","=<<","==",">",">=",">>",">>=","^","^^","||","*","*>","**"),t("builtin")("Applicative","Bool","Bounded","Char","Double","EQ","Either","Enum","Eq","False","FilePath","Float","Floating","Fractional","Functor","GT","IO","IOError","Int","Integer","Integral","Just","LT","Left","Maybe","Monad","Nothing","Num","Ord","Ordering","Rational","Read","ReadS","Real","RealFloat","RealFrac","Right","Show","ShowS","String","True"),t("builtin")("abs","acos","acosh","all","and","any","appendFile","asTypeOf","asin","asinh","atan","atan2","atanh","break","catch","ceiling","compare","concat","concatMap","const","cos","cosh","curry","cycle","decodeFloat","div","divMod","drop","dropWhile","either","elem","encodeFloat","enumFrom","enumFromThen","enumFromThenTo","enumFromTo","error","even","exp","exponent","fail","filter","flip","floatDigits","floatRadix","floatRange","floor","fmap","foldl","foldl1","foldr","foldr1","fromEnum","fromInteger","fromIntegral","fromRational","fst","gcd","getChar","getContents","getLine","head","id","init","interact","ioError","isDenormalized","isIEEE","isInfinite","isNaN","isNegativeZero","iterate","last","lcm","length","lex","lines","log","logBase","lookup","map","mapM","mapM_","max","maxBound","maximum","maybe","min","minBound","minimum","mod","negate","not","notElem","null","odd","or","otherwise","pi","pred","print","product","properFraction","pure","putChar","putStr","putStrLn","quot","quotRem","read","readFile","readIO","readList","readLn","readParen","reads","readsPrec","realToFrac","recip","rem","repeat","replicate","return","reverse","round","scaleFloat","scanl","scanl1","scanr","scanr1","seq","sequence","sequence_","show","showChar","showList","showParen","showString","shows","showsPrec","significand","signum","sin","sinh","snd","span","splitAt","sqrt","subtract","succ","sum","tail","take","takeWhile","tan","tanh","toEnum","toInteger","toRational","truncate","uncurry","undefined","unlines","until","unwords","unzip","unzip3","userError","words","writeFile","zip","zip3","zipWith","zipWith3"),e}();const k={name:"haskell",startState:function(){return{f:d}},copyState:function(e){return{f:e.f}},token:function(e,t){var r=t.f(e,(function(e){t.f=e})),n=e.current();return g.hasOwnProperty(n)?g[n]:r},languageData:{commentTokens:{line:"--",block:{open:"{-",close:"-}"}}}};t.haskell=k}}]);
diff --git a/jupyterlab-ihaskell/labextension/static/remoteEntry-33e79930ce2a88033aa3.js b/jupyterlab-ihaskell/labextension/static/remoteEntry-33e79930ce2a88033aa3.js
new file mode 100644
--- /dev/null
+++ b/jupyterlab-ihaskell/labextension/static/remoteEntry-33e79930ce2a88033aa3.js
@@ -0,0 +1,1 @@
+var _JUPYTERLAB;(()=>{"use strict";var e,r,t,n,o,a,i,l,u,s,f,p,d,h,c,v,g={494:(e,r,t)=>{var n={"./index":()=>t.e(568).then((()=>()=>t(568))),"./extension":()=>t.e(568).then((()=>()=>t(568)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),a=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>a})}},m={};function b(e){var r=m[e];if(void 0!==r)return r.exports;var t=m[e]={exports:{}};return g[e](t,t.exports,b),t.exports}b.m=g,b.c=m,b.d=(e,r)=>{for(var t in r)b.o(r,t)&&!b.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((r,t)=>(b.f[t](e,r),r)),[])),b.u=e=>e+"-ddb8cadba5e09aa373e6.js",b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r="jupyterlab-ihaskell:",b.l=(t,n,o,a)=>{if(e[t])e[t].push(n);else{var i,l;if(void 0!==o)for(var u=document.getElementsByTagName("script"),s=0;s<u.length;s++){var f=u[s];if(f.getAttribute("src")==t||f.getAttribute("data-webpack")==r+o){i=f;break}}i||(l=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,b.nc&&i.setAttribute("nonce",b.nc),i.setAttribute("data-webpack",r+o),i.src=t),e[t]=[n];var p=(r,n)=>{i.onerror=i.onload=null,clearTimeout(d);var o=e[t];if(delete e[t],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(n))),r)return r(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=p.bind(null,i.onerror),i.onload=p.bind(null,i.onload),l&&document.head.appendChild(i)}},(()=>{b.S={};var e={},r={};b.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];b.o(b.S,t)||(b.S[t]={});var a=b.S[t],i="jupyterlab-ihaskell",l=[];return"default"===t&&((e,r,t,n)=>{var o=a[e]=a[e]||{},l=o[r];(!l||!l.loaded&&(1!=!l.eager?n:i>l.from))&&(o[r]={get:()=>b.e(568).then((()=>()=>b(568))),from:i,eager:!1})})("jupyterlab-ihaskell","0.0.14"),e[t]=l.length?Promise.all(l).then((()=>e[t]=1)):1}}})(),(()=>{var e;b.g.importScripts&&(e=b.g.location+"");var r=b.g.document;if(!e&&r&&(r.currentScript&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&!e;)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),b.p=e})(),t=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},n=(e,r)=>{e=t(e),r=t(r);for(var n=0;;){if(n>=e.length)return n<r.length&&"u"!=(typeof r[n])[0];var o=e[n],a=(typeof o)[0];if(n>=r.length)return"u"==a;var i=r[n],l=(typeof i)[0];if(a!=l)return"o"==a&&"n"==l||"s"==l||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;n++}},o=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,a=1;a<e.length;a++)n--,t+="u"==(typeof(l=e[a]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(a=1;a<e.length;a++){var l=e[a];i.push(0===l?"not("+u()+")":1===l?"("+u()+" || "+u()+")":2===l?i.pop()+" "+i.pop():o(l))}return u();function u(){return i.pop().replace(/^\((.+)\)$/,"$1")}},a=(e,r)=>{if(0 in e){r=t(r);var n=e[0],o=n<0;o&&(n=-n-1);for(var i=0,l=1,u=!0;;l++,i++){var s,f,p=l<e.length?(typeof e[l])[0]:"";if(i>=r.length||"o"==(f=(typeof(s=r[i]))[0]))return!u||("u"==p?l>n&&!o:""==p!=o);if("u"==f){if(!u||"u"!=p)return!1}else if(u)if(p==f)if(l<=n){if(s!=e[l])return!1}else{if(o?s>e[l]:s<e[l])return!1;s!=e[l]&&(u=!1)}else if("s"!=p&&"n"!=p){if(o||l<=n)return!1;u=!1,l--}else{if(l<=n||f<p!=o)return!1;u=!1}else"s"!=p&&"n"!=p&&(u=!1,l--)}}var d=[],h=d.pop.bind(d);for(i=1;i<e.length;i++){var c=e[i];d.push(1==c?h()|h():2==c?h()&h():c?a(c,r):!h())}return!!h()},i=(e,r)=>{var t=b.S[e];if(!t||!b.o(t,r))throw new Error("Shared module "+r+" doesn't exist in shared scope "+e);return t},l=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&n(e,r)?r:e),0)},u=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+o(n)+")",s=(e,r,t,n)=>{var o=l(e,t);return a(n,o)||f(u(e,t,o,n)),p(e[t][o])},f=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},p=e=>(e.loaded=1,e.get()),d=(e=>function(r,t,n,o){var a=b.I(r);return a&&a.then?a.then(e.bind(e,r,b.S[r],t,n,o)):e(r,b.S[r],t,n)})(((e,r,t,n)=>(i(e,t),s(r,0,t,n)))),h={},c={373:()=>d("default","@codemirror/language",[1,6,0,0]),428:()=>d("default","@jupyterlab/codemirror",[1,4,0,11])},v={568:[373,428]},b.f.consumes=(e,r)=>{b.o(v,e)&&v[e].forEach((e=>{if(b.o(h,e))return r.push(h[e]);var t=r=>{h[e]=0,b.m[e]=t=>{delete b.c[e],t.exports=r()}},n=r=>{delete h[e],b.m[e]=t=>{throw delete b.c[e],r}};try{var o=c[e]();o.then?r.push(h[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}))},(()=>{var e={589:0};b.f.j=(r,t)=>{var n=b.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var a=b.p+b.u(r),i=new Error;b.l(a,(t=>{if(b.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+a+")",i.name="ChunkLoadError",i.type=o,i.request=a,n[1](i)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[a,i,l]=t,u=0;if(a.some((r=>0!==e[r]))){for(n in i)b.o(i,n)&&(b.m[n]=i[n]);l&&l(b)}for(r&&r(t);u<a.length;u++)o=a[u],b.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunkjupyterlab_ihaskell=self.webpackChunkjupyterlab_ihaskell||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})();var y=b(494);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB)["jupyterlab-ihaskell"]=y})();
diff --git a/jupyterlab-ihaskell/labextension/static/remoteEntry-ddc4c9d791a676e50bb9.js b/jupyterlab-ihaskell/labextension/static/remoteEntry-ddc4c9d791a676e50bb9.js
deleted file mode 100644
--- a/jupyterlab-ihaskell/labextension/static/remoteEntry-ddc4c9d791a676e50bb9.js
+++ /dev/null
@@ -1,1 +0,0 @@
-var _JUPYTERLAB;(()=>{"use strict";var e,r,t,n,o,a,i,l,u,s,f,p,h,c,d,v={494:(e,r,t)=>{var n={"./index":()=>t.e(568).then((()=>()=>t(568))),"./extension":()=>t.e(568).then((()=>()=>t(568)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),a=(e,r)=>{if(t.S){var n=t.S.default,o="default";if(n&&n!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[o]=e,t.I(o,r)}};t.d(r,{get:()=>o,init:()=>a})}},b={};function g(e){var r=b[e];if(void 0!==r)return r.exports;var t=b[e]={exports:{}};return v[e].call(t.exports,t,t.exports,g),t.exports}g.m=v,g.c=b,g.d=(e,r)=>{for(var t in r)g.o(r,t)&&!g.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},g.f={},g.e=e=>Promise.all(Object.keys(g.f).reduce(((r,t)=>(g.f[t](e,r),r)),[])),g.u=e=>e+"-1b6fb876268b3f3be3f4.js",g.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),g.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r="jupyterlab-ihaskell:",g.l=(t,n,o,a)=>{if(e[t])e[t].push(n);else{var i,l;if(void 0!==o)for(var u=document.getElementsByTagName("script"),s=0;s<u.length;s++){var f=u[s];if(f.getAttribute("src")==t||f.getAttribute("data-webpack")==r+o){i=f;break}}i||(l=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,g.nc&&i.setAttribute("nonce",g.nc),i.setAttribute("data-webpack",r+o),i.src=t),e[t]=[n];var p=(r,n)=>{i.onerror=i.onload=null,clearTimeout(h);var o=e[t];if(delete e[t],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(n))),r)return r(n)},h=setTimeout(p.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=p.bind(null,i.onerror),i.onload=p.bind(null,i.onload),l&&document.head.appendChild(i)}},(()=>{g.S={};var e={},r={};g.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];g.o(g.S,t)||(g.S[t]={});var a=g.S[t],i="jupyterlab-ihaskell",l=[];switch(t){case"default":((e,r,t,n)=>{var o=a[e]=a[e]||{},l=o[r];(!l||!l.loaded&&(1!=!l.eager?n:i>l.from))&&(o[r]={get:()=>g.e(568).then((()=>()=>g(568))),from:i,eager:!1})})("jupyterlab-ihaskell","0.0.14")}return e[t]=l.length?Promise.all(l).then((()=>e[t]=1)):1}}})(),(()=>{var e;g.g.importScripts&&(e=g.g.location+"");var r=g.g.document;if(!e&&r&&(r.currentScript&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");t.length&&(e=t[t.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),g.p=e})(),t=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},n=(e,r)=>{e=t(e),r=t(r);for(var n=0;;){if(n>=e.length)return n<r.length&&"u"!=(typeof r[n])[0];var o=e[n],a=(typeof o)[0];if(n>=r.length)return"u"==a;var i=r[n],l=(typeof i)[0];if(a!=l)return"o"==a&&"n"==l||"s"==l||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;n++}},o=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,a=1;a<e.length;a++)n--,t+="u"==(typeof(l=e[a]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(a=1;a<e.length;a++){var l=e[a];i.push(0===l?"not("+u()+")":1===l?"("+u()+" || "+u()+")":2===l?i.pop()+" "+i.pop():o(l))}return u();function u(){return i.pop().replace(/^\((.+)\)$/,"$1")}},a=(e,r)=>{if(0 in e){r=t(r);var n=e[0],o=n<0;o&&(n=-n-1);for(var i=0,l=1,u=!0;;l++,i++){var s,f,p=l<e.length?(typeof e[l])[0]:"";if(i>=r.length||"o"==(f=(typeof(s=r[i]))[0]))return!u||("u"==p?l>n&&!o:""==p!=o);if("u"==f){if(!u||"u"!=p)return!1}else if(u)if(p==f)if(l<=n){if(s!=e[l])return!1}else{if(o?s>e[l]:s<e[l])return!1;s!=e[l]&&(u=!1)}else if("s"!=p&&"n"!=p){if(o||l<=n)return!1;u=!1,l--}else{if(l<=n||f<p!=o)return!1;u=!1}else"s"!=p&&"n"!=p&&(u=!1,l--)}}var h=[],c=h.pop.bind(h);for(i=1;i<e.length;i++){var d=e[i];h.push(1==d?c()|c():2==d?c()&c():d?a(d,r):!c())}return!!c()},i=(e,r)=>{var t=g.S[e];if(!t||!g.o(t,r))throw new Error("Shared module "+r+" doesn't exist in shared scope "+e);return t},l=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&n(e,r)?r:e),0)},u=(e,r,t)=>"Unsatisfied version "+r+" of shared singleton module "+e+" (required "+o(t)+")",s=(e,r,t,n)=>{var o=l(e,t);return a(n,o)||"undefined"!=typeof console&&console.warn&&console.warn(u(t,o,n)),f(e[t][o])},f=e=>(e.loaded=1,e.get()),p=(e=>function(r,t,n,o){var a=g.I(r);return a&&a.then?a.then(e.bind(e,r,g.S[r],t,n,o)):e(r,g.S[r],t,n)})(((e,r,t,n)=>(i(e,t),s(r,0,t,n)))),h={},c={36:()=>p("default","@jupyterlab/codemirror",[1,3,0,7])},d={568:[36]},g.f.consumes=(e,r)=>{g.o(d,e)&&d[e].forEach((e=>{if(g.o(h,e))return r.push(h[e]);var t=r=>{h[e]=0,g.m[e]=t=>{delete g.c[e],t.exports=r()}},n=r=>{delete h[e],g.m[e]=t=>{throw delete g.c[e],r}};try{var o=c[e]();o.then?r.push(h[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}))},(()=>{var e={589:0};g.f.j=(r,t)=>{var n=g.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var a=g.p+g.u(r),i=new Error;g.l(a,(t=>{if(g.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+a+")",i.name="ChunkLoadError",i.type=o,i.request=a,n[1](i)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[a,i,l]=t,u=0;if(a.some((r=>0!==e[r]))){for(n in i)g.o(i,n)&&(g.m[n]=i[n]);l&&l(g)}for(r&&r(t);u<a.length;u++)o=a[u],g.o(e,o)&&e[o]&&e[o][0](),e[a[u]]=0},t=self.webpackChunkjupyterlab_ihaskell=self.webpackChunkjupyterlab_ihaskell||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})();var m=g(494);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB)["jupyterlab-ihaskell"]=m})();
diff --git a/jupyterlab-ihaskell/labextension/static/third-party-licenses.json b/jupyterlab-ihaskell/labextension/static/third-party-licenses.json
deleted file mode 100644
--- a/jupyterlab-ihaskell/labextension/static/third-party-licenses.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "packages": []
-}
diff --git a/main/Main.hs b/main/Main.hs
--- a/main/Main.hs
+++ b/main/Main.hs
@@ -113,6 +113,8 @@
       kernelSpecOpts { kernelSpecInstallPrefix = Just prefix }
     addFlag kernelSpecOpts KernelspecUseStack =
       kernelSpecOpts { kernelSpecUseStack = True }
+    addFlag kernelSpecOpts (KernelspecStackFlag flag) =
+      kernelSpecOpts { kernelSpecStackFlags = flag : (kernelSpecStackFlags kernelSpecOpts) }
     addFlag kernelSpecOpts (KernelspecEnvFile fp) =
       kernelSpecOpts { kernelSpecEnvFile = Just fp }
     addFlag _kernelSpecOpts flag = error $ "Unknown flag" ++ show flag
@@ -125,6 +127,7 @@
   let debug = kernelSpecDebug kOpts
       libdir = kernelSpecGhcLibdir kOpts
       useStack = kernelSpecUseStack kOpts
+      stackFlags = kernelSpecStackFlags kOpts
 
   -- Parse the profile file.
   let profileErr = error $ "ihaskell: "++profileSrc++": Failed to parse profile file"
@@ -142,14 +145,19 @@
             Left _ -> False
             Right (_, stackStdout, stackStderr) -> "The Haskell Tool Stack" `isInfixOf` (stackStdout ++ stackStderr)
 
+    when debug $ putStrLn ("Using stack: " <> show stack)
+
     -- If we're in a stack directory, use `stack` to set the environment
     -- We can't do this with base <= 4.6 because setEnv doesn't exist.
-    when stack $
-      readProcess "stack" ["exec", "env"] "" >>= parseAndSetEnv
+    when stack $ do
+      when debug $ putStrLn "Using environment from stack:"
+      readProcess "stack" (["exec", "env"] <> stackFlags) "" >>= parseAndSetEnv debug
 
   case kernelSpecEnvFile kOpts of
     Nothing -> return ()
-    Just envFile -> readFile envFile >>= parseAndSetEnv
+    Just envFile -> do
+      when debug $ putStrLn "Using environment from env file: "
+      readFile envFile >>= parseAndSetEnv debug
 
   -- Serve on all sockets and ports defined in the profile.
   interface <- serveProfile profile debug
@@ -226,11 +234,13 @@
 
     isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage]
 
-    parseAndSetEnv envLines =
+    parseAndSetEnv debug envLines =
       forM_ (lines envLines) $ \line -> do
         case break (== '=') line of
           (_, []) -> return ()
-          (key, _:val) -> setEnv key val
+          (key, _:val) -> do
+            when debug $ putStrLn ("\t" <> line)
+            setEnv key val
 
 -- Initial kernel state.
 initialKernelState :: KernelSpecOptions -> IO (MVar KernelState)
diff --git a/src/IHaskell/Eval/Completion.hs b/src/IHaskell/Eval/Completion.hs
--- a/src/IHaskell/Eval/Completion.hs
+++ b/src/IHaskell/Eval/Completion.hs
@@ -68,10 +68,8 @@
                     | Extension String
   deriving (Show, Eq)
 
-#if MIN_VERSION_ghc(8,2,0)
 exposedName :: (a, b) -> a
 exposedName = fst
-#endif
 
 extName :: FlagSpec flag -> String
 extName (FlagSpec { flagSpecName = name }) = name
diff --git a/src/IHaskell/Eval/Evaluate.hs b/src/IHaskell/Eval/Evaluate.hs
--- a/src/IHaskell/Eval/Evaluate.hs
+++ b/src/IHaskell/Eval/Evaluate.hs
@@ -21,6 +21,7 @@
 import           IHaskellPrelude
 
 import           Control.Concurrent (forkIO, threadDelay)
+import           Control.Monad.Trans.State (runStateT)
 import           Data.Foldable (foldMap)
 import           Prelude (head, tail, last, init)
 import qualified Data.Set as Set
@@ -106,18 +107,13 @@
 import           IHaskell.Eval.Lint
 #endif
 
-#if MIN_VERSION_ghc(8,4,0)
 import qualified Data.Text as Text
 import           IHaskell.Eval.Evaluate.HTML (htmlify)
-#endif
 
 #if MIN_VERSION_ghc(9,0,0)
 import           GHC.Data.FastString
-#elif MIN_VERSION_ghc(8,2,0)
-import           FastString (unpackFS)
 #else
-import           Paths_ihaskell (version)
-import           Data.Version (versionBranch)
+import           FastString (unpackFS)
 #endif
 
 #if MIN_VERSION_ghc(9,2,0)
@@ -256,7 +252,7 @@
       Just cfg -> let
         PackageName name = unitPackageName cfg
         in unpackFS name
-#elif MIN_VERSION_ghc(8,2,0)
+#else
 packageIdString' :: DynFlags -> PackageConfig -> String
 packageIdString' dflags pkg_cfg =
     case (lookupPackage dflags $ packageConfigId pkg_cfg) of
@@ -264,10 +260,6 @@
       Just cfg -> let
         PackageName name = packageName cfg
         in unpackFS name
-#else
-packageIdString' :: DynFlags -> PackageConfig -> String
-packageIdString' dflags pkg_cfg =
-    fromMaybe "(unknown)" (unitIdPackageIdString dflags $ packageConfigId pkg_cfg)
 #endif
 
 #if MIN_VERSION_ghc(9,4,0)
@@ -333,14 +325,8 @@
       initStr = "ihaskell-"
 #endif
 
-#if MIN_VERSION_ghc(8,2,0)
       -- Name of the ihaskell package, i.e. "ihaskell"
       iHaskellPkgName = "ihaskell"
-#else
-      -- Name of the ihaskell package, e.g. "ihaskell-1.2.3.4"
-      iHaskellPkgName = initStr ++ intercalate "." (map show (versionBranch version))
-#endif
-
       displayPkgs = [ pkgName
                     | pkgName <- packageNames
                     , Just (x:_) <- [stripPrefix initStr pkgName]
@@ -356,18 +342,9 @@
 
       importFmt = "import IHaskell.Display.%s"
 
-
-#if MIN_VERSION_ghc(8,2,0)
       toImportStmt :: String -> String
       toImportStmt = printf importFmt . concatMap capitalize . drop 1 . split "-"
-#else
-      dropFirstAndLast :: [a] -> [a]
-      dropFirstAndLast = reverse . drop 1 . reverse . drop 1
 
-      toImportStmt :: String -> String
-      toImportStmt = printf importFmt . concatMap capitalize . dropFirstAndLast . split "-"
-#endif
-
       displayImports = map toImportStmt displayPkgs
 
   void $ setSessionDynFlags $ dflgs { packageFlags = packageFlags dflgs ++ hiddenFlags }
@@ -435,7 +412,10 @@
          -> (KernelState -> [WidgetMsg] -> IO KernelState) -- ^ Function to handle widget messages
          -> Interpreter (KernelState, ErrorOccurred)
 evaluate kernelState code output widgetHandler = do
-  cmds <- parseString (cleanString code)
+  flags <- getSessionDynFlags
+  (cmds, flags') <- liftIO $ flip runStateT flags $ parseString (cleanString code)
+  void $ setSessionDynFlags flags'
+
   let execCount = getExecutionCounter kernelState
 
   -- Extract all parse errors.
@@ -516,10 +496,19 @@
 -- from inside the notebook environment.
 extractValue :: Typeable a => String -> Interpreter (Either String a)
 extractValue expr = do
+#if MIN_VERSION_ghc(9,0,0)
+  compiled <- gcatch (Right <$> dynCompileExpr expr) (\exc -> return (Left (show exc)))
+  case compiled of
+    Left exc -> return (Left exc)
+    Right dyn -> case fromDynamic dyn of
+      Nothing     -> return (Left multipleIHaskells)
+      Just result -> return (Right result)
+#else
   compiled <- dynCompileExpr expr
   case fromDynamic compiled of
     Nothing     -> return (Left multipleIHaskells)
     Just result -> return (Right result)
+#endif
 
   where
     multipleIHaskells =
@@ -941,11 +930,9 @@
       { evalStatus = Success
       , evalResult = Display [
           plain strings
-#if MIN_VERSION_ghc(8,4,0)
           , htmlify (Text.pack <$> htmlCodeWrapperClass state)
                     (Text.pack $ htmlCodeTokenPrefix state)
                     strings
-#endif
           ]
       , evalState = state
       , evalPager = []
@@ -994,28 +981,16 @@
   -- is no appropriate typeclass instance, this will throw an exception and thus `attempt` will return
   -- False, and we just resort to plaintext.
   let displayExpr = printf "(IHaskell.Display.display (%s))" expr :: String
-#if MIN_VERSION_ghc(8,2,0)
   canRunDisplay <- attempt $ exprType TM_Inst displayExpr
-#else
-  canRunDisplay <- attempt $ exprType displayExpr
-#endif
 
   -- Check if this is a widget.
   let widgetExpr = printf "(IHaskell.Display.Widget (%s))" expr :: String
-#if MIN_VERSION_ghc(8,2,0)
   isWidget <- attempt $ exprType TM_Inst widgetExpr
-#else
-  isWidget <- attempt $ exprType widgetExpr
-#endif
 
   -- Check if this is a template haskell declaration
   let declExpr = printf "((id :: IHaskellTH.DecsQ -> IHaskellTH.DecsQ) (%s))" expr :: String
   let anyExpr = printf "((id :: IHaskellPrelude.Int -> IHaskellPrelude.Int) (%s))" expr :: String
-#if MIN_VERSION_ghc(8,2,0)
   isTHDeclaration <- liftM2 (&&) (attempt $ exprType TM_Inst declExpr) (not <$> attempt (exprType TM_Inst anyExpr))
-#else
-  isTHDeclaration <- liftM2 (&&) (attempt $ exprType declExpr) (not <$> attempt (exprType anyExpr))
-#endif
 
   write state $ "Can Display: " ++ show canRunDisplay
   write state $ "Is Widget: " ++ show isWidget
@@ -1111,11 +1086,7 @@
                       then disp :: Display
                       else removeSvg disp
 
-#if MIN_VERSION_ghc(8,2,0)
     isIO exp = attempt $ exprType TM_Inst $ printf "((\\x -> x) :: IO a -> IO a) (%s)" exp
-#else
-    isIO exp = attempt $ exprType $ printf "((\\x -> x) :: IO a -> IO a) (%s)" exp
-#endif
 
     postprocessShowError :: EvalOut -> EvalOut
     postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps }
@@ -1161,11 +1132,7 @@
       -- Get all the type strings.
       dflags <- getSessionDynFlags
       types <- forM nonDataNames $ \name -> do
-#if MIN_VERSION_ghc(8,2,0)
                  theType <- showSDocUnqual dflags . ppr <$> exprType TM_Inst name
-#else
-                 theType <- showSDocUnqual dflags . ppr <$> exprType name
-#endif
                  return $ name ++ " :: " ++ theType
 
       return $ Display [html' (Just ihaskellCSS) $ unlines $ map formatGetType types]
@@ -1549,11 +1516,7 @@
         else do
           -- Get all the type strings.
           types <- forM nonItNames $ \name -> do
-#if MIN_VERSION_ghc(8,2,0)
                      theType <- showSDocUnqual dflags . ppr <$> exprType TM_Inst name
-#else
-                     theType <- showSDocUnqual dflags . ppr <$> exprType name
-#endif
                      return $ name ++ " :: " ++ theType
 
           let joined = unlines types
diff --git a/src/IHaskell/Eval/Info.hs b/src/IHaskell/Eval/Info.hs
--- a/src/IHaskell/Eval/Info.hs
+++ b/src/IHaskell/Eval/Info.hs
@@ -26,11 +26,7 @@
 info name = ghandle handler $ do
 #endif
   dflags <- getSessionDynFlags
-#if MIN_VERSION_ghc(8,2,0)
   result <- exprType TM_Inst name
-#else
-  result <- exprType name
-#endif
   return $ typeCleaner $ showPpr dflags result
   where
     handler :: SomeException -> Interpreter String
diff --git a/src/IHaskell/Eval/Parser.hs b/src/IHaskell/Eval/Parser.hs
--- a/src/IHaskell/Eval/Parser.hs
+++ b/src/IHaskell/Eval/Parser.hs
@@ -17,15 +17,12 @@
 
 import           IHaskellPrelude
 
+import           Control.Monad.Trans.State
 import           Data.Char (toLower)
 import           Data.List (maximumBy, inits)
 import           Prelude (head, tail)
 
-#if MIN_VERSION_ghc(8,4,0)
 import           GHC hiding (Located, Parsed)
-#else
-import           GHC hiding (Located)
-#endif
 
 import           Language.Haskell.GHC.Parser
 import           IHaskell.Eval.Util
@@ -74,16 +71,15 @@
   deriving (Show, Eq)
 
 -- | Parse a string into code blocks.
-parseString :: String -> Ghc [Located CodeBlock]
+parseString :: String -> StateT DynFlags IO [Located CodeBlock]
 parseString codeString = do
   -- Try to parse this as a single module.
-  flags' <- getSessionDynFlags
+  flags' <- get
   flags <- do
     result <- liftIO $ parsePragmasIntoDynFlags flags' "<interactive>" codeString
     return $ fromMaybe flags' result
-  _ <- setSessionDynFlags flags
-  let output = runParser flags parserModule codeString
-  case output of
+  put flags
+  case runParser flags parserModule codeString of
     Parsed mdl
       | Just _ <- hsmodName (unLoc mdl) -> return [Located 1 $ Module codeString]
     _ -> do
@@ -93,11 +89,11 @@
 
       -- Return to previous flags. When parsing, flags can be set to make sure parsing works properly. But
       -- we don't want those flags to be set during evaluation until the right time.
-      _ <- setSessionDynFlags flags
+      put flags
       return result
 
   where
-    parseChunk :: GhcMonad m => String -> LineNumber -> m (Located CodeBlock)
+    parseChunk :: String -> LineNumber -> StateT DynFlags IO (Located CodeBlock)
     parseChunk chunk ln = Located ln <$> handleChunk
       where
         handleChunk
@@ -105,7 +101,7 @@
           | isPragma chunk = return $ parsePragma chunk ln
           | otherwise = parseCodeChunk chunk ln
 
-    processChunks :: GhcMonad m => [Located CodeBlock] -> [Located String] -> m [Located CodeBlock]
+    processChunks :: [Located CodeBlock] -> [Located String] -> StateT DynFlags IO [Located CodeBlock]
     processChunks accum remaining =
       case remaining of
         -- If we have no more remaining lines, return the accumulated results.
@@ -125,7 +121,7 @@
     isPragma :: String -> Bool
     isPragma = isPrefixOf "{-#" . strip
 
-activateExtensions :: GhcMonad m => CodeBlock -> m ()
+activateExtensions :: CodeBlock -> StateT DynFlags IO ()
 activateExtensions (Directive SetExtension ext) = void $ setExtension ext
 activateExtensions (Directive SetDynFlag flags) =
   case stripPrefix "-X" flags of
@@ -133,16 +129,16 @@
     Nothing  -> return ()
 activateExtensions (Pragma PragmaLanguage exts) = void $ setAll exts
   where
-    setAll :: GhcMonad m => [String] -> m (Maybe String)
+    setAll :: [String] -> StateT DynFlags IO (Maybe String)
     setAll exts' = do
       errs <- mapM setExtension exts'
       return $ msum errs
 activateExtensions _ = return ()
 
 -- | Parse a single chunk of code, as indicated by the layout of the code.
-parseCodeChunk :: GhcMonad m => String -> LineNumber -> m CodeBlock
+parseCodeChunk :: String -> LineNumber -> StateT DynFlags IO CodeBlock
 parseCodeChunk code startLine = do
-  flags <- getSessionDynFlags
+  flags <- get
   let
       -- Try each parser in turn.
       rawResults = map (tryParser code) (parsers flags)
diff --git a/src/IHaskell/Eval/Util.hs b/src/IHaskell/Eval/Util.hs
--- a/src/IHaskell/Eval/Util.hs
+++ b/src/IHaskell/Eval/Util.hs
@@ -33,6 +33,8 @@
 import qualified Data.ByteString.Char8 as CBS
 #endif
 
+import           Control.Monad.Trans.State
+
 -- GHC imports.
 #if MIN_VERSION_ghc(9,8,0)
 import           GHC.Core.InstEnv (is_cls, is_tys, mkInstEnv, instEnvElts)
@@ -130,7 +132,7 @@
 import           StringUtils (replace)
 
 #if MIN_VERSION_ghc(9,0,0)
-#elif MIN_VERSION_ghc(8,4,0)
+#else
 import           CmdLineParser (warnMsg)
 #endif
 
@@ -223,10 +225,8 @@
     default_dflags = defaultDynFlags (settings dflags) (llvmConfig dflags)
 #elif MIN_VERSION_ghc(8,6,0)
     default_dflags = defaultDynFlags (settings dflags) (llvmTargets dflags, llvmPasses dflags)
-#elif MIN_VERSION_ghc(8,4,0)
-    default_dflags = defaultDynFlags (settings dflags) (llvmTargets dflags)
 #else
-    default_dflags = defaultDynFlags (settings dflags)
+    default_dflags = defaultDynFlags (settings dflags) (llvmTargets dflags)
 #endif
 
     fstr, fnostr :: String -> O.SDoc
@@ -283,10 +283,8 @@
       defaultDynFlags (settings dflags) (llvmConfig dflags) `lang_set`
 #elif MIN_VERSION_ghc(8,6,0)
       defaultDynFlags (settings dflags) (llvmTargets dflags, llvmPasses dflags) `lang_set`
-#elif MIN_VERSION_ghc(8,4,0)
-      defaultDynFlags (settings dflags) (llvmTargets dflags) `lang_set`
 #else
-      defaultDynFlags (settings dflags) `lang_set`
+      defaultDynFlags (settings dflags) (llvmTargets dflags) `lang_set`
 #endif
       case language dflags of
         Nothing -> Just Haskell2010
@@ -294,13 +292,12 @@
 
 -- | Set an extension and update flags. Return @Nothing@ on success. On failure, return an error
 -- message.
-setExtension :: GhcMonad m => String -> m (Maybe String)
+setExtension :: String -> StateT DynFlags IO (Maybe String)
 setExtension ext = do
-  flags <- getSessionDynFlags
   case extensionFlag ext of
     Nothing -> return $ Just $ "Could not parse extension name: " ++ ext
     Just flag -> do
-      _ <- setSessionDynFlags $
+      modify $ \flags ->
         case flag of
           SetFlag ghcFlag   -> xopt_set flags ghcFlag
           UnsetFlag ghcFlag -> xopt_unset flags ghcFlag
@@ -339,10 +336,8 @@
   let noParseErrs = map (("Could not parse: " ++) . unLoc) unrecognized
 #if MIN_VERSION_ghc(9,8,0)
       allWarns = map (show . flip O.runSDoc O.defaultSDocContext . E.formatBulleted . diagnosticMessage defaultOpts . errMsgDiagnostic) (bagToList $ getWarningMessages warnings) ++
-#elif MIN_VERSION_ghc(8,4,0)
-      allWarns = map (unLoc . warnMsg) warnings ++
 #else
-      allWarns = map unLoc warnings ++
+      allWarns = map (unLoc . warnMsg) warnings ++
 #endif
         -- Stack appears to duplicate package flags, so we use `nub` to work around this
         ["-package not supported yet" | nub (packageFlags flags) /= nub (packageFlags flags0)]
@@ -363,10 +358,8 @@
 #endif
 #if MIN_VERSION_ghc(9,0,0)
   let style = O.mkUserStyle unqual O.AllTheWay
-#elif MIN_VERSION_ghc(8,2,0)
-  let style = O.mkUserStyle flags unqual O.AllTheWay
 #else
-  let style = O.mkUserStyle unqual O.AllTheWay
+  let style = O.mkUserStyle flags unqual O.AllTheWay
 #endif
   let cols = pprCols flags
 #if MIN_VERSION_ghc(9,2,0)
@@ -416,7 +409,6 @@
   let flag = flip xopt_set
       unflag = flip xopt_unset
       dflags = flag ExtendedDefaultRules . unflag MonomorphismRestriction $ setWayDynFlag originalFlags
-#if MIN_VERSION_ghc(8,2,0)
       pkgFlags =
         case sandboxPackages of
           Nothing -> packageDBFlags originalFlags
@@ -440,22 +432,7 @@
     , pprCols = 300
     , packageDBFlags = pkgFlags
     }
-#else
-      pkgConfs =
-        case sandboxPackages of
-          Nothing -> extraPkgConfs originalFlags
-          Just path ->
-            let pkg = PkgConfFile path
-            in (pkg :) . extraPkgConfs originalFlags
 
-  void $ setSessionDynFlags $ dflags
-    { hscTarget = HscInterpreted
-    , ghcLink = LinkInMemory
-    , pprCols = 300
-    , extraPkgConfs = pkgConfs
-    }
-#endif
-
 -- | Evaluate a single import statement. If this import statement is importing a module which was
 -- previously imported implicitly (such as `Prelude`) or if this module has a `hiding` annotation,
 -- the previous import is removed.
@@ -477,11 +454,7 @@
 
   where
     -- Check whether an import is the same as another import (same module).
-#if MIN_VERSION_ghc(8,4,0)
     importOf :: ImportDecl GhcPs -> InteractiveImport -> Bool
-#else
-    importOf :: ImportDecl RdrName -> InteractiveImport -> Bool
-#endif
     importOf _ (IIModule _) = False
     importOf imp (IIDecl decl) =
 #if MIN_VERSION_ghc(8,10,0)
@@ -495,12 +468,8 @@
     implicitImportOf :: ImportDecl GhcPs -> InteractiveImport -> Bool
     implicitImportOf _ (IIModule _) = False
     implicitImportOf imp (IIDecl decl) = ideclImplicit (ideclExt decl) && imp `importOf` IIDecl decl
-#elif MIN_VERSION_ghc(8,4,0)
-    implicitImportOf :: ImportDecl GhcPs -> InteractiveImport -> Bool
-    implicitImportOf _ (IIModule _) = False
-    implicitImportOf imp (IIDecl decl) = ideclImplicit decl && imp `importOf` IIDecl decl
 #else
-    implicitImportOf :: ImportDecl RdrName -> InteractiveImport -> Bool
+    implicitImportOf :: ImportDecl GhcPs -> InteractiveImport -> Bool
     implicitImportOf _ (IIModule _) = False
     implicitImportOf imp (IIDecl decl) = ideclImplicit decl && imp `importOf` IIDecl decl
 #endif
@@ -512,14 +481,8 @@
       case ideclImportList imp of
         Just (EverythingBut, _) -> True
         _              -> False
-#elif MIN_VERSION_ghc(8,4,0)
-    isHiddenImport :: ImportDecl GhcPs -> Bool
-    isHiddenImport imp =
-      case ideclHiding imp of
-        Just (True, _) -> True
-        _              -> False
 #else
-    isHiddenImport :: ImportDecl RdrName -> Bool
+    isHiddenImport :: ImportDecl GhcPs -> Bool
     isHiddenImport imp =
       case ideclHiding imp of
         Just (True, _) -> True
@@ -572,11 +535,7 @@
 -- | Get the type of an expression and convert it to a string.
 getType :: GhcMonad m => String -> m String
 getType expr = do
-#if MIN_VERSION_ghc(8,2,0)
   result <- exprType TM_Inst expr
-#else
-  result <- exprType expr
-#endif
   flags <- getSessionDynFlags
 #if MIN_VERSION_ghc(9,2,0)
   let typeStr = showSDoc flags $ O.ppr result
@@ -616,26 +575,12 @@
   where
 
     getInfo' = getInfo False
-
-#if MIN_VERSION_ghc(8,4,0)
     getInfoType (theType, _, _, _, _) = theType
-#else
-    getInfoType (theType, _, _, _) = theType
-#endif
-
-#if MIN_VERSION_ghc(8,4,0)
     printInfo (thing, fixity, classInstances, famInstances, _) =
       pprTyThingInContextLoc thing O.$$
       showFixity thing fixity O.$$
       O.vcat (map GHC.pprInstance classInstances) O.$$
       O.vcat (map GHC.pprFamInst famInstances)
-#else
-    printInfo (thing, fixity, classInstances, famInstances) =
-      pprTyThingInContextLoc thing O.$$
-      showFixity thing fixity O.$$
-      O.vcat (map GHC.pprInstance classInstances) O.$$
-      O.vcat (map GHC.pprFamInst famInstances)
-#endif
     showFixity thing fixity =
       if fixity == GHC.defaultFixity
         then O.empty
diff --git a/src/IHaskell/Eval/Widgets.hs b/src/IHaskell/Eval/Widgets.hs
--- a/src/IHaskell/Eval/Widgets.hs
+++ b/src/IHaskell/Eval/Widgets.hs
@@ -193,32 +193,29 @@
       where
 #if MIN_VERSION_aeson(2,0,0)
         nestedLookupRemove :: BufferPath -> Value -> (Value, Maybe Value)
-        nestedLookupRemove [] v = (v,Just v)
-        nestedLookupRemove [b] v =
-          case v of
-            Object o -> (Object $ KM.delete (Key.fromText b) o, KM.lookup (Key.fromText b) o)
-            _ -> (v, Nothing)
-        nestedLookupRemove (b:bp) v =
-          case v of
-            Object o -> maybe (v,Nothing) (upd . nestedLookupRemove bp) (KM.lookup (Key.fromText b) o)
-            _ -> (v,Nothing)
-            where upd :: (Value, Maybe Value) -> (Value, Maybe Value)
-                  upd (Object v', Just (Object u)) = (Object $ KM.insert (Key.fromText b) (Object u) v', Just $ Object u)
-                  upd r = r
+        nestedLookupRemove [] v = (v, Nothing)
+        nestedLookupRemove (k:ks) (Object obj) =
+          case KM.lookup key obj of
+               Just subObj ->
+                 if null ks
+                 then (Object $ KM.delete key obj, Just subObj)
+                 else let (newSubObj, removed) = nestedLookupRemove ks subObj
+                      in (Object $ KM.insert key newSubObj obj, removed)
+               Nothing -> (Object obj, Nothing)
+          where key = Key.fromText k
+        nestedLookupRemove _ v = (v, Nothing)
 #else
         nestedLookupRemove :: BufferPath -> Value -> (Value, Maybe Value)
-        nestedLookupRemove [] v = (v,Just v)
-        nestedLookupRemove [b] v =
-          case v of
-            Object o -> (Object $ HM.delete b o, HM.lookup b o)
-            _ -> (v, Nothing)
-        nestedLookupRemove (b:bp) v =
-          case v of
-            Object o -> maybe (v,Nothing) (upd . nestedLookupRemove bp) (HM.lookup b o)
-            _ -> (v,Nothing)
-            where upd :: (Value, Maybe Value) -> (Value, Maybe Value)
-                  upd (Object v', Just (Object u)) = (Object $ HM.insert b (Object u) v', Just $ Object u)
-                  upd r = r
+        nestedLookupRemove [] v = (v, Nothing)
+        nestedLookupRemove (k:ks) (Object obj) =
+          case HM.lookup k obj of
+            Just subObj ->
+              if null ks
+              then (Object $ HM.delete k obj, Just subObj)
+              else let (newSubObj, removed) = nestedLookupRemove ks subObj
+                   in (Object $ HM.insert k newSubObj obj, removed)
+            Nothing -> (Object obj, Nothing)
+        nestedLookupRemove _ v = (v, Nothing)
 #endif
 
         f :: (Value, [ByteString], [BufferPath]) -> BufferPath -> (Value, [ByteString], [BufferPath])
diff --git a/src/IHaskell/Flags.hs b/src/IHaskell/Flags.hs
--- a/src/IHaskell/Flags.hs
+++ b/src/IHaskell/Flags.hs
@@ -43,6 +43,7 @@
               | ConvertLhsStyle (LhsStyle String)
               | KernelspecInstallPrefix String
               | KernelspecUseStack
+              | KernelspecStackFlag String
               | KernelspecEnvFile FilePath
   deriving (Eq, Show)
 
@@ -153,6 +154,10 @@
   where
     addStack (Args md prev) = Args md (KernelspecUseStack : prev)
 
+kernelStackExtraFlags :: Flag Args
+kernelStackExtraFlags = flagReq ["stack-flag"] (store KernelspecStackFlag) ""
+        "Extra flag to pass to `stack` when --stack is used. Can be specified multiple times."
+
 kernelEnvFileFlag :: Flag Args
 kernelEnvFileFlag =
   flagReq
@@ -181,7 +186,7 @@
 installKernelSpec :: Mode Args
 installKernelSpec =
   mode "install" (Args InstallKernelSpec []) "Install the Jupyter kernelspec." noArgs
-    [ghcLibFlag, ghcRTSFlag, kernelDebugFlag, kernelNameFlag, displayNameFlag, confFlag, installPrefixFlag, helpFlag, kernelStackFlag, kernelEnvFileFlag]
+    [ghcLibFlag, ghcRTSFlag, kernelDebugFlag, kernelNameFlag, displayNameFlag, confFlag, installPrefixFlag, helpFlag, kernelStackFlag, kernelStackExtraFlags, kernelEnvFileFlag]
 
 kernel :: Mode Args
 kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg
@@ -189,6 +194,7 @@
            , kernelDebugFlag
            , confFlag
            , kernelStackFlag
+           , kernelStackExtraFlags
            , kernelEnvFileFlag
            , kernelCodeMirrorFlag
            , kernelHtmlCodeWrapperClassFlag
diff --git a/src/IHaskell/IPython.hs b/src/IHaskell/IPython.hs
--- a/src/IHaskell/IPython.hs
+++ b/src/IHaskell/IPython.hs
@@ -45,6 +45,7 @@
          , kernelSpecConfFile :: IO (Maybe String)        -- ^ Filename of profile JSON file.
          , kernelSpecInstallPrefix :: Maybe String
          , kernelSpecUseStack :: Bool                     -- ^ Whether to use @stack@ environments.
+         , kernelSpecStackFlags :: [String]               -- ^ Extra flags to pass to @stack@.
          , kernelSpecEnvFile :: Maybe FilePath
          , kernelSpecKernelName :: String                 -- ^ The IPython kernel name
          , kernelSpecDisplayName :: String                -- ^ The IPython kernel display name
@@ -62,6 +63,7 @@
   , kernelSpecConfFile = defaultConfFile
   , kernelSpecInstallPrefix = Nothing
   , kernelSpecUseStack = False
+  , kernelSpecStackFlags = []
   , kernelSpecEnvFile = Nothing
   , kernelSpecKernelName = "haskell"
   , kernelSpecDisplayName = "Haskell"
@@ -150,6 +152,7 @@
              [] -> []
              _ -> "+RTS" : kernelSpecRTSOptions opts ++ ["-RTS"])
            ++ ["--stack" | kernelSpecUseStack opts]
+           ++ mconcat [["--stack-flag", f] | f <- kernelSpecStackFlags opts]
 
   let kernelSpec = KernelSpec
         { kernelDisplayName = kernelSpecDisplayName opts
diff --git a/src/tests/Hspec.hs b/src/tests/Hspec.hs
--- a/src/tests/Hspec.hs
+++ b/src/tests/Hspec.hs
@@ -1,6 +1,10 @@
 module Main where
 
 import           Prelude
+import           Control.Monad (when)
+import           System.Directory (doesPathExist, getCurrentDirectory)
+import           System.Environment (lookupEnv, setEnv)
+import           Data.Maybe (fromMaybe)
 
 import           Test.Hspec
 
@@ -10,7 +14,12 @@
 import           IHaskell.Test.Hoogle (testHoogle)
 
 main :: IO ()
-main =
+main = do
+  currentDir <- getCurrentDirectory
+  packageConfInPlaceExists <- doesPathExist (currentDir ++ "/dist/package.conf.inplace")
+  when packageConfInPlaceExists $ do
+    ghcPackagePath <- fromMaybe "" <$> lookupEnv "GHC_PACKAGE_PATH"
+    setEnv "GHC_PACKAGE_PATH" $ (currentDir ++ "/dist/package.conf.inplace/" ++ ":" ++ ghcPackagePath)
   hspec $ do
     testParser
     testEval
diff --git a/src/tests/IHaskell/Test/Eval.hs b/src/tests/IHaskell/Test/Eval.hs
--- a/src/tests/IHaskell/Test/Eval.hs
+++ b/src/tests/IHaskell/Test/Eval.hs
@@ -151,41 +151,34 @@
 #elif MIN_VERSION_ghc(9,0,0)
       -- brackets around the type variable
       ":typ 3" `becomes` ["3 :: forall {p}. Num p => p"]
-#elif MIN_VERSION_ghc(8,2,0)
+#else
       -- It's `p` instead of `t` for some reason
       ":typ 3" `becomes` ["3 :: forall p. Num p => p"]
-#else
-      ":typ 3" `becomes` ["3 :: forall t. Num t => t"]
 #endif
 
     it "evaluates :k directive" $ do
       ":k Maybe" `becomes` ["Maybe :: * -> *"]
 
     it "evaluates :in directive" $ do
-#if MIN_VERSION_ghc(8,10,0)
-      displayDatasBecome ":in String" [
-        ManyDisplay [Display [
-                        DisplayData PlainText "type String :: *\ntype String = [Char]\n  \t-- Defined in \8216GHC.Base\8217"
-                        , DisplayData MimeHtml "<div class=\"code CodeMirror cm-s-jupyter cm-s-ipython\"><span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">::</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">*</span><span class=\"cm-space\"><br /></span>\n<span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">=</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">[</span><span class=\"cm-variable-2\">Char</span><span class=\"cm-atom\">]</span><span class=\"cm-space\"><br />  \t</span><span class=\"cm-comment\">-- Defined in \8216GHC.Base\8217</span><span class=\"cm-space\"><br /></span></div>"
-                        ]]
-        ]
-#elif MIN_VERSION_ghc(8,4,0)
+#if MIN_VERSION_ghc(9,10,0)
       displayDatasBecome ":in String" [
         ManyDisplay [Display [
-                        DisplayData PlainText "type String = [Char] \t-- Defined in \8216GHC.Base\8217"
-                        , DisplayData MimeHtml "<div class=\"code CodeMirror cm-s-jupyter cm-s-ipython\"><span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">=</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">[</span><span class=\"cm-variable-2\">Char</span><span class=\"cm-atom\">]</span><span class=\"cm-space\"> \t</span><span class=\"cm-comment\">-- Defined in \8216GHC.Base\8217</span><span class=\"cm-space\"><br /></span></div>"
+                        DisplayData PlainText "type String :: *\ntype String = [Char]\n  \t-- Defined in \8216GHC.Internal.Base\8217"
+                        , DisplayData MimeHtml "<div class=\"code CodeMirror cm-s-jupyter cm-s-ipython\"><span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">::</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">*</span><span class=\"cm-space\"><br /></span>\n<span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">=</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">[</span><span class=\"cm-variable-2\">Char</span><span class=\"cm-atom\">]</span><span class=\"cm-space\"><br />  \t</span><span class=\"cm-comment\">-- Defined in \8216GHC.Internal.Base\8217</span><span class=\"cm-space\"><br /></span></div>"
                         ]]
         ]
-#elif MIN_VERSION_ghc(8,2,0)
+#elif MIN_VERSION_ghc(8,10,0)
       displayDatasBecome ":in String" [
         ManyDisplay [Display [
-                        DisplayData PlainText "type String = [Char] \t-- Defined in \8216GHC.Base\8217"
+                        DisplayData PlainText "type String :: *\ntype String = [Char]\n  \t-- Defined in \8216GHC.Base\8217"
+                        , DisplayData MimeHtml "<div class=\"code CodeMirror cm-s-jupyter cm-s-ipython\"><span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">::</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">*</span><span class=\"cm-space\"><br /></span>\n<span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">=</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">[</span><span class=\"cm-variable-2\">Char</span><span class=\"cm-atom\">]</span><span class=\"cm-space\"><br />  \t</span><span class=\"cm-comment\">-- Defined in \8216GHC.Base\8217</span><span class=\"cm-space\"><br /></span></div>"
                         ]]
         ]
 #else
       displayDatasBecome ":in String" [
         ManyDisplay [Display [
                         DisplayData PlainText "type String = [Char] \t-- Defined in \8216GHC.Base\8217"
+                        , DisplayData MimeHtml "<div class=\"code CodeMirror cm-s-jupyter cm-s-ipython\"><span class=\"cm-keyword\">type</span><span class=\"cm-space\"> </span><span class=\"cm-variable-2\">String</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">=</span><span class=\"cm-space\"> </span><span class=\"cm-atom\">[</span><span class=\"cm-variable-2\">Char</span><span class=\"cm-atom\">]</span><span class=\"cm-space\"> \t</span><span class=\"cm-comment\">-- Defined in \8216GHC.Base\8217</span><span class=\"cm-space\"><br /></span></div>"
                         ]]
         ]
 #endif
diff --git a/src/tests/IHaskell/Test/Parser.hs b/src/tests/IHaskell/Test/Parser.hs
--- a/src/tests/IHaskell/Test/Parser.hs
+++ b/src/tests/IHaskell/Test/Parser.hs
@@ -4,6 +4,8 @@
 
 import           Prelude
 
+import           Control.Monad.Trans.State (evalStateT)
+import           GHC (getSessionDynFlags)
 import           Text.RawString.QQ (r)
 
 import           Test.Hspec
@@ -15,14 +17,17 @@
 import           IHaskell.Eval.ParseShell (parseShell)
 
 parses :: String -> IO [CodeBlock]
-parses str = map unloc <$> ghc (parseString str)
+parses str = do
+  flags <- ghc getSessionDynFlags
+  map unloc <$> evalStateT (parseString str) flags
 
 like :: (Show a, Eq a) => IO a -> a -> IO ()
 like parser desired = parser >>= (`shouldBe` desired)
 
 is :: String -> (String -> CodeBlock) -> IO ()
 is string blockType = do
-  result <- ghc $ parseString string
+  flags <- ghc getSessionDynFlags
+  result <- evalStateT (parseString string) flags
   map unloc result `shouldBe` [blockType $ strip string]
 
 testParser :: Spec
@@ -180,7 +185,7 @@
   it "parses a type signature followed by a declaration" $
     "fun :: [a] -> Int\nfun [] = 10\nfun (x : xs) = 100" `is` Declaration
 
-  it "parases a simple module" $
+  it "parses a simple module" $
     "module A where x = 3" `is` Module
 
   it "parses a module with an export" $
@@ -220,7 +225,8 @@
         Just 100))
     |] `is` Expression
   it "correctly locates parsed items" $ do
-    ghc (parseString
+    flags <- ghc getSessionDynFlags
+    (flip evalStateT flags $ parseString
       [r|
         first
 
