1module PscIdeClient where
5import Data.Array as Array
6import Data.Either (Either(..), either)
7import Data.Foldable (intercalate)
8import Data.Maybe (Maybe(..))
9import Data.Newtype (class Newtype, un, unwrap)
10import Data.Posix.Signal (Signal(..))
11import Data.String.Utils (endsWith)
12import Debug.Trace (spy)
14import Effect.Aff (Aff, Canceler, effectCanceler, makeAff)
15import Effect.Class (liftEffect)
16import Effect.Class.Console (log)
17import Effect.Exception (message)
18import Effect.Ref (Ref)
19import Effect.Ref as Ref
20import Foreign (renderForeignError)
21import Node.Buffer as Buf
22import Node.ChildProcess (ChildProcess)
23import Node.ChildProcess as CP
24import Node.Encoding (Encoding(..))
25import Node.FS.Aff (writeTextFile)
26import Node.Net.Socket (Socket)
27import Node.Net.Socket as Socket
28import Playground.Playground (Folder(..))
29import Shared.Models.Body (CompileResult)
30import Simple.JSON (readJSON, writeJSON)
39buildCommand ∷ BuildCommand
41 { "command": "rebuild"
42 , "params": { "file": "src/Main.purs" }
45closeSocketCanceller ∷ Socket -> Canceler
46closeSocketCanceller s =
49 (Socket.destroyed s) do
50 -- Socket.end s (pure unit)
51 log $ "Destroying socket..."
52 Socket.destroy s Nothing
54startIdeServer ∷ Folder -> Int -> Aff ChildProcess
55startIdeServer folder port = do
57 $ "Spawning ide server in folder "
61 cp <- spawnProcess folder ("npx purs ide server -p " <> show port)
63 $ "Spawned ide server in folder "
69restartIdeServer ∷ Folder -> Int -> Ref ChildProcess -> Aff Unit
70restartIdeServer folder port processRef = do
71 cp <- Ref.read processRef # liftEffect
72 CP.kill SIGKILL cp # liftEffect
73 newCp <- startIdeServer folder port
74 Ref.write newCp processRef # liftEffect
76spawnProcess ∷ Folder -> String -> Aff ChildProcess
77spawnProcess folder command =
78 makeAff \callback -> do
80 options = CP.defaultExecOptions { cwd = Just (un Folder folder) }
81 childProcess <- CP.exec command options mempty
82 callback (Right childProcess)
83 pure $ effectCanceler ((log $ "Killing " <> show (CP.pid childProcess)) *> CP.kill SIGKILL childProcess)
85newtype PscIdeConnection
87 { serverProcess ∷ ChildProcess
92derive instance ntPscIdeConnection :: Newtype PscIdeConnection _
93instance eqPscIdeConnection :: Eq PscIdeConnection where
94 eq c1 c2 = (unwrap c1).port == (unwrap c2).port
96saveMainFile ∷ Folder -> String -> Aff Unit
97saveMainFile folder code = writeTextFile UTF8 (un Folder folder <> "/src/Main.purs") code
99getFolder ∷ PscIdeConnection -> Folder
100getFolder (PscIdeConnection { folder }) = folder
102compileCode ∷ String -> PscIdeConnection -> Aff String
103compileCode code (PscIdeConnection { port, folder }) = do
104 saveMainFile folder code
107 "Compiling code in " <> (un Folder folder) <> " on port " <> show port
108 socket <- Socket.createConnectionTCP port "localhost" mempty
110 Socket.onError socket (affCb <<< Left)
111 resultRef <- Ref.new ""
112 Socket.onData socket \dataFromServer -> do
113 str <- dataFromServer # either (Buf.toString UTF8) pure
114 newStr <- resultRef # Ref.modify (_ <> str)
115 when (newStr # endsWith "\n") do
116 Socket.endString socket "" UTF8 mempty
118 $ Socket.onClose socket case _ of
119 true -> mempty -- should be covered in onError
121 res <- Ref.read resultRef
124 command = writeJSON buildCommand <> "\n"
125 Socket.onReady socket (void $ Socket.writeString socket command UTF8 mempty)
126 pure (closeSocketCanceller socket)
128mkConnection ∷ Folder -> Int -> Aff PscIdeConnection
129mkConnection folder port = do
130 serverProcess <- startIdeServer folder port
131 liftEffect $ CP.onError serverProcess \e ->
132 log $ "Server process on port " <> show port <> " got error " <> show e
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list