Skip to content

Commit

Permalink
Improve UI responsiveness by moving language service into web worker
Browse files Browse the repository at this point in the history
+ Add the `LanguageServiceWorker` module with a compilation entry point to build the language service in a self-contained app.
+ Make the JavaScript compiled from the worker app available on a dedicated HTTP path.
+ Add the language service worker to the page.
+ Change the front end to replace the direct application of language service functions with requests to the worker.
  • Loading branch information
Viir committed Apr 26, 2024
1 parent 7d96534 commit 65fb9d9
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 43 deletions.
49 changes: 49 additions & 0 deletions implement/example-apps/elm-editor/src/Backend/Main.elm
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ updateForHttpRequestEventExceptRequestsToVolatileProcess httpRequestEvent stateB
(staticContentHttpHeaders { contentType = "text/javascript", contentEncoding = Nothing })
|> continueWithStaticHttpResponse

Just (Backend.Route.StaticFileRoute Backend.Route.FrontendLanguageServiceWorkerJavaScriptRoute) ->
httpResponseOkWithBodyAsBase64
(Just languageServiceWorkerJavaScriptBase64)
(staticContentHttpHeaders { contentType = "text/javascript", contentEncoding = Nothing })
|> continueWithStaticHttpResponse

Just (Backend.Route.StaticFileRoute Backend.Route.MonacoFrameDocumentRoute) ->
httpResponseOkWithStringContent monacoHtmlDocument
(staticContentHttpHeaders { contentType = "text/html", contentEncoding = Nothing })
Expand All @@ -298,6 +304,30 @@ updateForHttpRequestEventExceptRequestsToVolatileProcess httpRequestEvent stateB
)


languageServiceWorkerJavaScript : String
languageServiceWorkerJavaScript =
CompilationInterface.ElmMake.elm_make____src_LanguageServiceWorker_elm.javascript.utf8
++ """
const app = Elm.LanguageServiceWorker.init();
onmessage = function ({ data }) {
app.ports.receiveRequest.send(data);
};
app.ports.sendResponse.subscribe(function(response) {
console.log(response);
postMessage(response);
})
"""


languageServiceWorkerJavaScriptBase64 : String
languageServiceWorkerJavaScriptBase64 =
Maybe.withDefault "Failed encoding as base64"
(Base64.fromString languageServiceWorkerJavaScript)


updateForRequestToVolatileProcessResult :
String
-> Platform.WebService.RequestToVolatileProcessResult
Expand Down Expand Up @@ -499,6 +529,25 @@ window.addEventListener('message', function(e) {
}, false);
if (window.Worker) {
const langServiceWorker = new Worker("language-service-worker.js");
app.ports.sendRequestToLanguageService.subscribe(function(message) {
langServiceWorker.postMessage(message);
});
function receiveResponseFromLanguageService(message) {
app.ports.receiveResponseFromLanguageService?.send(message);
}
langServiceWorker.onmessage = function(message) {
console.log('Message received from language service worker');
receiveResponseFromLanguageService(message.data);
}
} else {
console.error("Failed to set up language service: Browser doesn't support web workers.");
}
</script>
</html>
Expand Down
7 changes: 7 additions & 0 deletions implement/example-apps/elm-editor/src/Backend/Route.elm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Route
type StaticFile
= FrontendHtmlDocumentRoute { debug : Bool }
| FrontendElmJavascriptRoute { debug : Bool }
| FrontendLanguageServiceWorkerJavaScriptRoute
| MonacoFrameDocumentRoute


Expand All @@ -37,12 +38,18 @@ fromAuth0LoginRedirectPath =
"/login/fromauth0"


languageServiceWorkerJavascriptPath : String
languageServiceWorkerJavascriptPath =
"language-service-worker.js"


routeFromUrl : Url.Url -> Maybe Route
routeFromUrl =
Url.Parser.parse
(Url.Parser.oneOf
[ Url.Parser.map (StaticFileRoute (FrontendElmJavascriptRoute { debug = False })) (Url.Parser.s elmMadeScriptFileNameDefault)
, Url.Parser.map (StaticFileRoute (FrontendElmJavascriptRoute { debug = True })) (Url.Parser.s elmMadeScriptFileNameDebug)
, Url.Parser.map (StaticFileRoute FrontendLanguageServiceWorkerJavaScriptRoute) (Url.Parser.s languageServiceWorkerJavascriptPath)
, Url.Parser.map (StaticFileRoute MonacoFrameDocumentRoute) (Url.Parser.s "monaco")
, Url.Parser.map ApiRoute (Url.Parser.s "api")
, Url.Parser.map (StaticFileRoute (FrontendHtmlDocumentRoute { debug = True })) (Url.Parser.s "enable-elm-debug")
Expand Down
30 changes: 30 additions & 0 deletions implement/example-apps/elm-editor/src/Common.elm
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,33 @@ commonPrefixLength listA listB =

else
0


resultListMapCombine :
(item -> Result err itemOk)
-> List item
-> Result err (List itemOk)
resultListMapCombine mapItem list =
resultListMapCombineHelper [] mapItem list


resultListMapCombineHelper :
List itemOk
-> (item -> Result err itemOk)
-> List item
-> Result err (List itemOk)
resultListMapCombineHelper completeList mapItem sourceList =
case sourceList of
[] ->
Ok (List.reverse completeList)

item :: tail ->
case mapItem item of
Ok itemOk ->
resultListMapCombineHelper
(itemOk :: completeList)
mapItem
tail

Err err ->
Err err
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ elm_make____src_Frontend_Main_elm =
{ javascript = { base64 = "The compiler replaces this declaration." }
, debug = { javascript = { base64 = "The compiler replaces this declaration." } }
}


elm_make____src_LanguageServiceWorker_elm : { javascript : { utf8 : String } }
elm_make____src_LanguageServiceWorker_elm =
{ javascript = { utf8 = "The compiler replaces this declaration." }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Frontend.MonacoEditor
import FrontendBackendInterface
import Json.Decode
import Json.Encode
import LanguageServiceInterface
import WorkspaceState_2021_01


Expand Down Expand Up @@ -69,3 +70,23 @@ jsonEncodeWorkspaceStateDiff_2021_01 =
jsonDecodeWorkspaceStateDiff_2021_01 : Json.Decode.Decoder WorkspaceState_2021_01.WorkspaceStateDifference
jsonDecodeWorkspaceStateDiff_2021_01 =
Json.Decode.fail "The compiler replaces this declaration."


jsonEncodeLanguageServiceRequestInWorkspace : LanguageServiceInterface.RequestInWorkspaceWithId -> Json.Encode.Value
jsonEncodeLanguageServiceRequestInWorkspace =
always (Json.Encode.string "The compiler replaces this declaration.")


jsonDecodeLanguageServiceRequestInWorkspace : Json.Decode.Decoder LanguageServiceInterface.RequestInWorkspaceWithId
jsonDecodeLanguageServiceRequestInWorkspace =
Json.Decode.fail "The compiler replaces this declaration."


jsonEncodeLanguageServiceResponse : LanguageServiceInterface.ResponseWithId -> Json.Encode.Value
jsonEncodeLanguageServiceResponse =
always (Json.Encode.string "The compiler replaces this declaration.")


jsonDecodeLanguageServiceResponse : Json.Decode.Decoder LanguageServiceInterface.ResponseWithId
jsonDecodeLanguageServiceResponse =
Json.Decode.fail "The compiler replaces this declaration."
23 changes: 23 additions & 0 deletions implement/example-apps/elm-editor/src/FileTree.elm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module FileTree exposing (..)

import Common
import Dict
import List
import Tuple
Expand Down Expand Up @@ -157,6 +158,28 @@ mapBlobs mapBlob node =
BlobNode (mapBlob blob)


mapBlobsOrReturnFirstError : (a -> Result err b) -> FileTreeNode a -> Result ( List String, err ) (FileTreeNode b)
mapBlobsOrReturnFirstError tryMapBlob node =
case node of
TreeNode tree ->
Common.resultListMapCombine
(\( childName, childNode ) ->
mapBlobsOrReturnFirstError tryMapBlob childNode
|> Result.mapError (Tuple.mapFirst ((::) childName))
|> Result.map (Tuple.pair childName)
)
tree
|> Result.map TreeNode

BlobNode blob ->
case tryMapBlob blob of
Err err ->
Err ( [], err )

Ok mappedBlob ->
Ok (BlobNode mappedBlob)


mapBlobsWithPath : (( List String, a ) -> b) -> FileTreeNode a -> FileTreeNode b
mapBlobsWithPath =
mapBlobsWithPathWithPrefix []
Expand Down
13 changes: 13 additions & 0 deletions implement/example-apps/elm-editor/src/FileTreeInWorkspace.elm
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,16 @@ blobValueFromBytes asBytes =
|> Base64.fromBytes
|> Maybe.withDefault "Error encoding in base64"
}


blobValueFromBase64 : String -> Result String BlobNodeWithCache
blobValueFromBase64 asBase64 =
case Base64.toBytes asBase64 of
Nothing ->
Err "Failed to decode as base64"

Just asBytes ->
Ok
{ asBytes = asBytes
, asBase64 = asBase64
}
Loading

0 comments on commit 65fb9d9

Please sign in to comment.