diff --git a/packages.dhall b/packages.dhall index 0cadead..2269ca4 100644 --- a/packages.dhall +++ b/packages.dhall @@ -1,5 +1,5 @@ let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220706/packages.dhall sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031 + https://github.com/purescript/package-sets/releases/download/psc-0.15.14-20240227/packages.dhall sha256:c9633eb78193aac138d7debbc907bfedb8f2e3025ef5a874b8dbc1f35b75eef4 in upstream with uuid = @@ -41,9 +41,36 @@ in upstream } with literals.repo = "https://github.com/ilyakooo0/purescript-literals.git" with literals.version = "6875fb28026595cfb780318305a77e79b098bb01" - - with psc-ide.version = "0f16603b3336340cbdbde197671cb96ff969a5f8" - -- purescript-language-cst-parser v0.12.1 with bugfixes is not yet in the package set. + with psc-ide = + { dependencies = + [ "aff" + , "argonaut" + , "argonaut-codecs" + , "argonaut-core" + , "arrays" + , "bifunctors" + , "control" + , "datetime" + , "effect" + , "either" + , "exceptions" + , "foldable-traversable" + , "foreign-object" + , "integers" + , "maybe" + , "node-buffer" + , "node-child-process" + , "node-fs" + , "node-path" + , "nullable" + , "parallel" + , "prelude" + , "random" + , "strings" + ] + , repo = "https://github.com/kritzcreek/purescript-psc-ide" + , version = "5cc2cd48d067f72a760b970080d0ef0a4b427fdf" + } with language-cst-parser = { dependencies = [ "arrays" @@ -66,4 +93,4 @@ in upstream ] , repo = "https://github.com/natefaubion/purescript-language-cst-parser.git" , version = "v0.12.1" - } \ No newline at end of file + } diff --git a/spago.dhall b/spago.dhall index d02ee96..6d06e07 100644 --- a/spago.dhall +++ b/spago.dhall @@ -36,7 +36,6 @@ You can edit this file as you like. , "node-buffer" , "node-child-process" , "node-fs" - , "node-fs-aff" , "node-path" , "node-process" , "node-streams" diff --git a/src/IdePurescript/Build.purs b/src/IdePurescript/Build.purs index c1e3f7c..eaed305 100644 --- a/src/IdePurescript/Build.purs +++ b/src/IdePurescript/Build.purs @@ -25,7 +25,10 @@ import Node.Buffer (Buffer) import Node.Buffer as Buffer import Node.ChildProcess (ChildProcess) import Node.ChildProcess as CP +import Node.ChildProcess.Types (Exit(..), enableShell) import Node.Encoding as Encoding +import Node.Errors.SystemError (toError) +import Node.EventEmitter (on_, once_) import Node.Process (getEnv) import Node.Stream as S import PscIde as P @@ -69,8 +72,8 @@ spawn { command: Command cmd args, directory, useNpmDir } = do cmd' <- (fromMaybe cmd <<< Array.head) <$> whichSync { path, pathExt: Nothing } cmd - CP.spawn cmd' args - (CP.defaultSpawnOptions { cwd = Just directory, env = env }) + CP.spawn' cmd' args + (_ { cwd = Just directory, env = env, shell = Just enableShell }) -- Spawn with npm path, "which" call (windows support) and version info gathering spawnWithVersion :: @@ -87,9 +90,9 @@ spawnWithVersion { command: Command cmd args, directory, useNpmDir } = do childEnv = Object.insert (getPathProp env) (either identity identity pathVar) env - Just <$> CP.spawn cmdBin args - ( CP.defaultSpawnOptions - { cwd = Just directory, env = Just childEnv } + Just <$> CP.spawn' cmdBin args + ( _ + { cwd = Just directory, env = Just childEnv, shell = Just enableShell } ) _ -> pure Nothing pure { cmdBins, cp } @@ -113,17 +116,17 @@ build logCb buildOptions@{ command: Command cmd args } = do Just cp -> do logCb Info $ "Running build command: " <> intercalate " " (cmd : args) - CP.onError cp (cb <<< Left <<< CP.toStandardError) + cp # once_ CP.errorH (cb <<< Left <<< toError) errOutput <- Ref.new [] outOutput <- Ref.new [] let res :: Ref (Array Buffer) -> Buffer -> Effect Unit res r s = Ref.modify_ (_ `Array.snoc` s) r - catchException err $ S.onData (CP.stderr cp) (res errOutput) - catchException err $ S.onData (CP.stdout cp) (res outOutput) - CP.onClose cp + catchException err $ (CP.stderr cp) # on_ S.dataH (res errOutput) + catchException err $ (CP.stdout cp) # on_ S.dataH (res outOutput) + cp # once_ CP.closeH ( \exit -> case exit of - CP.Normally n + Normally n | n == 0 || n == 1 -> do pursError <- Ref.read errOutput >>= Buffer.concat >>= Buffer.toString Encoding.UTF8 diff --git a/src/IdePurescript/Exec.purs b/src/IdePurescript/Exec.purs index 4957286..67193b7 100644 --- a/src/IdePurescript/Exec.purs +++ b/src/IdePurescript/Exec.purs @@ -10,6 +10,7 @@ import Effect (Effect) import Effect.Aff (Aff) import Effect.Class (liftEffect) import Foreign.Object as Object +import Node.ChildProcess.Types (enableShell) import Node.Path as Path import Node.Process (getEnv, lookupEnv) import Node.Which (which') @@ -23,6 +24,7 @@ findBins pathVar server = do , path: either (const Nothing) Just pathVar , env: either (const Nothing) (Just <<< flip (Object.insert "PATH") env) pathVar + , shell: Just enableShell } server diff --git a/src/IdePurescript/PscIdeServer.purs b/src/IdePurescript/PscIdeServer.purs index ca42701..25b96cc 100644 --- a/src/IdePurescript/PscIdeServer.purs +++ b/src/IdePurescript/PscIdeServer.purs @@ -26,12 +26,15 @@ import Effect.Aff (Aff, attempt, try) import Effect.Class (liftEffect) import IdePurescript.Exec (findBins, getPathVar) import IdePurescript.PscIde (cwd) as PscIde +import Node.Buffer as Buffer import Node.ChildProcess (ChildProcess, stderr, stdout) +import Node.ChildProcess.Types (enableShell) import Node.Encoding (Encoding(..)) +import Node.EventEmitter (on_) import Node.Path (normalize) import Node.Platform (Platform(..)) import Node.Process (platform) -import Node.Stream (onDataString) +import Node.Stream (dataH) import PscIde.Server (Executable(Executable), LogLevel, defaultServerArgs, getSavedPort, pickFreshPort, savePort) import PscIde.Server as S @@ -61,12 +64,12 @@ type Notify = ErrorLevel -> String -> Effect Unit data Version = Version Int Int Int (Maybe String) parseVersion :: String -> Maybe Version -parseVersion s0 = +parseVersion s0 = let s = String.trim s0 dotted = String.takeWhile (\c -> c /= String.codePointFromChar ' ' && c /= String.codePointFromChar '-') s rest = String.trim <$> String.stripPrefix (Pattern dotted) s - + in case traverse Int.fromString $ split (Pattern ".") dotted of Just [ a, b, c ] -> Just $ Version a b c rest @@ -172,8 +175,14 @@ startServer' settings@({ exe: server }) path addNpmBin cb logCb = do wireOutput :: ChildProcess -> Notify -> Effect Unit wireOutput cp log = do - onDataString (stderr cp) UTF8 (log Warning) - onDataString (stdout cp) UTF8 (log Info) + logData stderr Warning + logData stdout Info + where + logData std level = + (std cp) # on_ dataH \buf -> do + str <- Buffer.toString UTF8 buf + log level str + -- | Start a psc-ide server instance, or find one already running on the expected port, checking if it has the right path. startServer :: Notify -> ServerSettings -> String -> Aff ServerStartResult @@ -222,6 +231,7 @@ startServer , source = glob , logLevel = logLevel , outputDirectory = outputDirectory + , shell = Just enableShell } ) where diff --git a/src/LanguageServer/IdePurescript/Formatting.purs b/src/LanguageServer/IdePurescript/Formatting.purs index 4e33c4e..2affe47 100644 --- a/src/LanguageServer/IdePurescript/Formatting.purs +++ b/src/LanguageServer/IdePurescript/Formatting.purs @@ -30,8 +30,10 @@ import LanguageServer.Protocol.Uri (uriToFilename) import Node.Buffer (Buffer) import Node.Buffer as Buffer import Node.ChildProcess as CP +import Node.ChildProcess.Types (Exit(..)) import Node.Encoding (Encoding(..)) -import Node.Encoding as Encoding +import Node.Errors.SystemError (toError) +import Node.EventEmitter (on_, once_) import Node.Stream as S getFormattedDocument :: @@ -106,29 +108,29 @@ format _logCb settings state formatter text = do , directory , useNpmDir: Config.addNpmPath settings } - CP.onError cp (err <<< CP.toStandardError) + cp # once_ CP.errorH (err <<< toError) result <- Ref.new [] let res :: Buffer -> Effect Unit res s = Ref.modify_ (_ `Array.snoc` s) result - catchException err $ S.onDataString (CP.stderr cp) Encoding.UTF8 $ - err <<< error - catchException err $ S.onData (CP.stdout cp) res - CP.onClose cp \exit -> case exit of - CP.Normally n + + catchException err $ (CP.stderr cp) # on_ S.dataH + (\b -> Buffer.toString UTF8 b >>= (err <<< error)) + catchException err $ (CP.stdout cp) # on_ S.dataH res + + cp # once_ CP.closeH \exit -> case exit of + Normally n | n == 0 || n == 1 -> Ref.read result >>= Buffer.concat - >>= Buffer.toString Encoding.UTF8 + >>= Buffer.toString UTF8 >>= succ _ -> do let Command cmd _ = command err $ error $ cmd <> " process exited abnormally" + when (not $ Foreign.isUndefined $ unsafeToForeign $ CP.pid cp) do catchException err $ void $ S.writeString (CP.stdin cp) UTF8 text - ( const - $ pure unit - ) - catchException err $ S.end (CP.stdin cp) (const $ pure unit) + catchException err $ S.end (CP.stdin cp) pure mempty _ -> pure "" diff --git a/src/LanguageServer/IdePurescript/Main.purs b/src/LanguageServer/IdePurescript/Main.purs index 2553dc1..a5f4486 100644 --- a/src/LanguageServer/IdePurescript/Main.purs +++ b/src/LanguageServer/IdePurescript/Main.purs @@ -616,7 +616,7 @@ main = do case maybeArgs of Nothing -> do Console.error "Error parsing args" - Process.exit 1 + Process.exit' 1 Just { version: true } -> do v <- version Console.log v diff --git a/src/LanguageServer/IdePurescript/Rename.purs b/src/LanguageServer/IdePurescript/Rename.purs index 0f932b9..2359c96 100644 --- a/src/LanguageServer/IdePurescript/Rename.purs +++ b/src/LanguageServer/IdePurescript/Rename.purs @@ -77,7 +77,7 @@ renameIdentifier docs settings state ({ textDocument, position, newName }) = do $ makeMultiWorkspaceEdit clientCapabilities (toMultiEdits usgEdits) -- Means that we deal with a local identifier. -- Not implemented. - Just ({ found: Left { } }) -> do + Just ({ found: Left {} }) -> do pure wsEditEmpty Nothing -> do @@ -311,7 +311,6 @@ getTextEdits typeInfo usages docsToEdit newText oldName = } ) - --| Uses purs ide to find information about definition and usages of rename target given its -- symbolic name and its type position. -- @@ -332,10 +331,10 @@ getTypeInfoWithUsages port word wordPos qualifier moduleState = do -- Find where the target is defined. typeInfos <- (eitherToErr $ P.type' port word moduleFilters moduleState.main) + -- Find its usages. First, try to check if if the target is in the defined -- positions. Then for each defined position find usages until they contain -- the target. - case findInDefined typeInfos of Just info -> getUsages info <#> Just <<< (/\) info @@ -617,12 +616,13 @@ findWordInRange (Pattern word) text range = where isOp = maybe false (not <<< flip Regex.test word) $ hush $ Regex.regex ("[a-z]") Regex.ignoreCase + nonNameSymbolRe = "[^A-Za-z\\d_]?" searchIndex line -- For operators use just search in string, for identifiers - regexp to -- match the whole word | isOp = String.indexOf (Pattern word) line | otherwise = - (hush $ Regex.regex ("\\b" <> word <> "\\b") Regex.noFlags) >>= + (hush $ Regex.regex ("\\b" <> word <> nonNameSymbolRe) Regex.noFlags) >>= flip Regex.search line getTextAtRangeInLines :: Array String -> Range -> String diff --git a/src/LanguageServer/IdePurescript/Server.purs b/src/LanguageServer/IdePurescript/Server.purs index 04d2a39..8a5e66b 100644 --- a/src/LanguageServer/IdePurescript/Server.purs +++ b/src/LanguageServer/IdePurescript/Server.purs @@ -26,7 +26,8 @@ import LanguageServer.IdePurescript.Config (ConfigFn) import LanguageServer.IdePurescript.Config as Config import LanguageServer.Protocol.Types (Settings) import Node.Buffer (toString) -import Node.ChildProcess (defaultExecOptions, execFile) +import Node.ChildProcess as CP +import Node.ChildProcess.Types (enableShell) import Node.Encoding (Encoding(..)) import Node.Process (lookupEnv) import PscIde (load) @@ -105,8 +106,8 @@ getPackagerPaths enabled binName settings root = Just (Executable bin _) -> makeAff \cb -> do void - $ execFile bin [ "sources" ] - (defaultExecOptions { cwd = Just root }) + $ CP.execFile' bin [ "sources" ] + (_ { cwd = Just root, shell = Just enableShell }) ( \{ stdout } -> do text <- toString UTF8 stdout cb $ pure $ lines text diff --git a/test/Fixture/RenameA.js b/test/Fixture/RenameA.js new file mode 100644 index 0000000..51a1512 --- /dev/null +++ b/test/Fixture/RenameA.js @@ -0,0 +1 @@ +export const fNewT = 10 \ No newline at end of file diff --git a/test/Fixture/RenameA.purs b/test/Fixture/RenameA.purs index a05d899..b8cc74f 100644 --- a/test/Fixture/RenameA.purs +++ b/test/Fixture/RenameA.purs @@ -8,6 +8,7 @@ module Test.Fixture.RenameA , Tup(..) , (/\) , type (/\) + , func' ) where type TypeSyn :: Type @@ -23,8 +24,16 @@ func1 :: Int -> TypeSyn func1 int = int +func' :: Int +func' = 1 + +f = func' + +newT :: Newt newT = Newt 10 +foreign import fNewT :: Newt + local1 :: Int local1 = func1 10 diff --git a/test/Fixture/RenameB.purs b/test/Fixture/RenameB.purs index 5f3e44c..15a851f 100644 --- a/test/Fixture/RenameB.purs +++ b/test/Fixture/RenameB.purs @@ -23,4 +23,4 @@ newT :: Newt newT = Newt 1 tup :: Int /\ String -tup = 1 /\ "1" \ No newline at end of file +tup = 1 /\ "1" diff --git a/test/Rename.purs b/test/Rename.purs index 6919c78..672c460 100644 --- a/test/Rename.purs +++ b/test/Rename.purs @@ -31,7 +31,7 @@ import Node.Process as Process import PscIde as PscIde import PscIde.Command (TypePosition(..)) import PureScript.CST (parseModule) -import Test.Spec (it) +import Test.Spec (it, itOnly) import Test.Spec as S import Test.Spec.Assertions as A import Test.Spec.Reporter (consoleReporter) @@ -214,7 +214,7 @@ prepare = do cwdDir <- liftEffect $ Process.cwd let rootPath = cwdDir <> "/test" - let settings = unsafeToForeign {} + let settings = unsafeToForeign {} --{ purescript: {"pscIdelogLevel": "debug"} } startRes <- PursIde.startServer' settings rootPath notify notify case startRes of @@ -294,8 +294,10 @@ prepare = do _ -> liftEffect $ throw "Could not start ide server" where + --notify _ str = Console.log str notify = emptyNotify + -- | Test Rename.getTextEdits function. renameSpec :: PrepareResult -> S.Spec Unit renameSpec prep = do @@ -327,6 +329,10 @@ renameSpec prep = do (moduleA /\ "func1 10" /\ "func1") expectedFunc + testRename itOnly "name with quote" + (moduleA /\ "func' =" /\ "func'") + [ moduleA /\ "func' ::" /\ "func'"] + let expectedTypeSyn = [ moduleA /\ "type TypeSyn =" /\ "TypeSyn" -- def @@ -378,6 +384,8 @@ renameSpec prep = do [ moduleA /\ "newtype Newt ::" /\ "Newt" -- def kind , moduleA /\ "newtype Newt =" /\ "Newt" -- def , moduleA /\ "Newt(Newt" /\ "Newt" -- export + , moduleA /\ "newT :: Newt" /\ "Newt" + , moduleA /\ "fNewT :: Newt" /\ "Newt" -- foreign import , moduleB /\ "Newt(Newt" /\ "Newt" --import , moduleB /\ "newT ::" /\ "Newt" ] @@ -535,8 +543,9 @@ renameSpec prep = do <$> (getTypePos path text (LinePattern lPtn) (Pattern ident)) replaceNewName ident iPtn = + (String.replace (Pattern ident) (Replacement (newName ident)) iPtn) main :: Effect Unit main = launchAff_ do - prepare >>= runSpec [ consoleReporter ] <<< renameSpec + prepare >>= runSpec [ consoleReporter ] <<< renameSpec