mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Enhanced completions for the assistant (#4420)
* Enhanced completions for the assistant changelog_begin - [DAML Assistant] The assistant can now do completions for SDK commands, e.g., ``daml ledger upl<TAB>`` will complete to ``daml ledger upload-dar``. changelog_end * Apply suggestions from code review Co-Authored-By: associahedron <231829+associahedron@users.noreply.github.com> Co-authored-by: associahedron <231829+associahedron@users.noreply.github.com>
This commit is contained in:
parent
30afc76d23
commit
fc87953ed4
@ -63,8 +63,8 @@ sdkVersionFromSdkConfig :: SdkConfig -> Either ConfigError SdkVersion
|
|||||||
sdkVersionFromSdkConfig = querySdkConfigRequired ["version"]
|
sdkVersionFromSdkConfig = querySdkConfigRequired ["version"]
|
||||||
|
|
||||||
-- | Read sdk config to get list of sdk commands.
|
-- | Read sdk config to get list of sdk commands.
|
||||||
listSdkCommands :: SdkConfig -> Either ConfigError [SdkCommandInfo]
|
listSdkCommands :: SdkPath -> EnrichedCompletion -> SdkConfig -> Either ConfigError [SdkCommandInfo]
|
||||||
listSdkCommands = querySdkConfigRequired ["commands"]
|
listSdkCommands sdkPath enriched sdkConf = map (\f -> f sdkPath enriched) <$> querySdkConfigRequired ["commands"] sdkConf
|
||||||
|
|
||||||
-- | Query the daml config by passing a path to the desired property.
|
-- | Query the daml config by passing a path to the desired property.
|
||||||
-- See 'queryConfig' for more details.
|
-- See 'queryConfig' for more details.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
-- Copyright (c) 2020 The DAML Authors. All rights reserved.
|
-- Copyright (c) 2020 The DAML Authors. All rights reserved.
|
||||||
-- SPDX-License-Identifier: Apache-2.0
|
-- SPDX-License-Identifier: Apache-2.0
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
|
||||||
module DA.Daml.Project.Types
|
module DA.Daml.Project.Types
|
||||||
( module DA.Daml.Project.Types
|
( module DA.Daml.Project.Types
|
||||||
@ -136,12 +136,34 @@ data SdkCommandInfo = SdkCommandInfo
|
|||||||
, sdkCommandPath :: SdkCommandPath -- ^ file path of binary relative to sdk directory
|
, sdkCommandPath :: SdkCommandPath -- ^ file path of binary relative to sdk directory
|
||||||
, sdkCommandArgs :: SdkCommandArgs -- ^ extra args to pass before user-supplied args (defaults to [])
|
, sdkCommandArgs :: SdkCommandArgs -- ^ extra args to pass before user-supplied args (defaults to [])
|
||||||
, sdkCommandDesc :: Maybe Text -- ^ description of sdk command (optional)
|
, sdkCommandDesc :: Maybe Text -- ^ description of sdk command (optional)
|
||||||
|
, sdkCommandForwardCompletion :: ForwardCompletion -- ^ Can we forward optparse-applicative completions to
|
||||||
|
-- this command
|
||||||
|
, sdkCommandSdkPath :: SdkPath -- ^ SDK path so we can get the absolute path to the command.
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
instance Y.FromJSON SdkCommandInfo where
|
data ForwardCompletion
|
||||||
parseJSON = Y.withObject "SdkCommandInfo" $ \p ->
|
= Forward EnrichedCompletion -- ^ Forward completions
|
||||||
SdkCommandInfo
|
| NoForward -- ^ No forwarding, fall back to basic completion
|
||||||
<$> (p Y..: "name")
|
deriving (Eq, Show)
|
||||||
<*> (p Y..: "path")
|
|
||||||
<*> fmap (fromMaybe (SdkCommandArgs [])) (p Y..:? "args")
|
-- | True if --bash-completion-enriched was part of argv.
|
||||||
<*> (p Y..:? "desc")
|
newtype EnrichedCompletion = EnrichedCompletion { getEnrichedCompletion :: Bool }
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
hasEnrichedCompletion :: [String] -> EnrichedCompletion
|
||||||
|
hasEnrichedCompletion = EnrichedCompletion . elem "--bash-completion-enriched"
|
||||||
|
|
||||||
|
instance Y.FromJSON (SdkPath -> EnrichedCompletion -> SdkCommandInfo) where
|
||||||
|
parseJSON = Y.withObject "SdkCommandInfo" $ \p -> do
|
||||||
|
name <- p Y..: "name"
|
||||||
|
path <- p Y..: "path"
|
||||||
|
args <- fmap (fromMaybe (SdkCommandArgs [])) (p Y..:? "args")
|
||||||
|
desc <- p Y..:? "desc"
|
||||||
|
completion <- fromMaybe False <$> p Y..:? "completion"
|
||||||
|
return $ \sdkPath enriched -> SdkCommandInfo
|
||||||
|
name
|
||||||
|
path
|
||||||
|
args
|
||||||
|
desc
|
||||||
|
(if completion then Forward enriched else NoForward)
|
||||||
|
sdkPath
|
||||||
|
@ -14,6 +14,7 @@ import DA.Daml.Assistant.Command
|
|||||||
import DA.Daml.Assistant.Version
|
import DA.Daml.Assistant.Version
|
||||||
import DA.Daml.Assistant.Install
|
import DA.Daml.Assistant.Install
|
||||||
import DA.Daml.Assistant.Util
|
import DA.Daml.Assistant.Util
|
||||||
|
import System.Environment (getArgs)
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.Process.Typed
|
import System.Process.Typed
|
||||||
@ -53,24 +54,26 @@ main = displayErrors $ do
|
|||||||
-- So if we can't find it, let the user know. This will happen whenever
|
-- So if we can't find it, let the user know. This will happen whenever
|
||||||
-- auto-install is disabled and the project or environment specify a
|
-- auto-install is disabled and the project or environment specify a
|
||||||
-- missing SDK version.
|
-- missing SDK version.
|
||||||
when (isNothing envSdkPath) $ do
|
case envSdkPath of
|
||||||
let installTarget
|
Nothing -> do
|
||||||
| Just v <- envSdkVersion = versionToString v
|
let installTarget
|
||||||
| otherwise = "latest"
|
| Just v <- envSdkVersion = versionToString v
|
||||||
hPutStr stderr . unlines $
|
| otherwise = "latest"
|
||||||
[ "DAML SDK not installed. Cannot run command without SDK."
|
hPutStr stderr . unlines $
|
||||||
, "To proceed, please install the SDK by running:"
|
[ "DAML SDK not installed. Cannot run command without SDK."
|
||||||
, ""
|
, "To proceed, please install the SDK by running:"
|
||||||
, " daml install " <> installTarget
|
, ""
|
||||||
, ""
|
, " daml install " <> installTarget
|
||||||
]
|
, ""
|
||||||
exitFailure
|
]
|
||||||
|
exitFailure
|
||||||
sdkConfig <- readSdkConfig (fromJust envSdkPath)
|
Just sdkPath -> do
|
||||||
sdkCommands <- fromRightM throwIO (listSdkCommands sdkConfig)
|
sdkConfig <- readSdkConfig sdkPath
|
||||||
userCommand <- getCommand sdkCommands
|
enriched <- hasEnrichedCompletion <$> getArgs
|
||||||
versionChecks env
|
sdkCommands <- fromRightM throwIO (listSdkCommands sdkPath enriched sdkConfig)
|
||||||
handleCommand env userCommand
|
userCommand <- getCommand sdkCommands
|
||||||
|
versionChecks env
|
||||||
|
handleCommand env userCommand
|
||||||
|
|
||||||
-- | Perform version checks, i.e. warn user if project SDK version or assistant SDK
|
-- | Perform version checks, i.e. warn user if project SDK version or assistant SDK
|
||||||
-- versions are out of date with the latest known release.
|
-- versions are out of date with the latest known release.
|
||||||
|
@ -18,8 +18,10 @@ import DA.Daml.Assistant.Types
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
|
import Options.Applicative.Types
|
||||||
import Options.Applicative.Extended
|
import Options.Applicative.Extended
|
||||||
import System.Environment
|
import System.Environment
|
||||||
|
import System.FilePath
|
||||||
import Data.Either.Extra
|
import Data.Either.Extra
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import System.Process
|
import System.Process
|
||||||
@ -60,8 +62,30 @@ dispatch info = subcommand
|
|||||||
(unwrapSdkCommandName $ sdkCommandName info)
|
(unwrapSdkCommandName $ sdkCommandName info)
|
||||||
(fromMaybe "" $ sdkCommandDesc info)
|
(fromMaybe "" $ sdkCommandDesc info)
|
||||||
forwardOptions
|
forwardOptions
|
||||||
(Dispatch info . UserCommandArgs <$>
|
(Dispatch info . UserCommandArgs <$> sdkCommandArgsParser info)
|
||||||
many (strArgument (metavar "ARGS" <> completer defaultCompleter)))
|
|
||||||
|
sdkCommandArgsParser :: SdkCommandInfo -> Parser [String]
|
||||||
|
sdkCommandArgsParser info = fromM (go (unwrapSdkCommandArgs $ sdkCommandArgs info))
|
||||||
|
where go args = do
|
||||||
|
mx <- oneM $ optional $ strArgument $ completer $
|
||||||
|
case sdkCommandForwardCompletion info of
|
||||||
|
Forward enriched -> nestedCompl enriched args
|
||||||
|
NoForward -> defaultCompleter
|
||||||
|
case mx of
|
||||||
|
Nothing -> return []
|
||||||
|
Just x -> (x :) <$> go (args ++ [x])
|
||||||
|
nestedCompl enriched args = mkCompleter $ \arg -> do
|
||||||
|
let path = unwrapSdkPath (sdkCommandSdkPath info) </> unwrapSdkCommandPath (sdkCommandPath info)
|
||||||
|
let createProc = proc
|
||||||
|
path
|
||||||
|
( [ "--bash-completion-enriched" | getEnrichedCompletion enriched ]
|
||||||
|
<>
|
||||||
|
("--bash-completion-index"
|
||||||
|
: show (length args + 1)
|
||||||
|
: concatMap (\x -> ["--bash-completion-word", x]) ("daml" : args ++ [arg])
|
||||||
|
))
|
||||||
|
stdout <- readCreateProcess createProc (repeat ' ')
|
||||||
|
pure $ lines stdout
|
||||||
|
|
||||||
commandParser :: [SdkCommandInfo] -> Parser Command
|
commandParser :: [SdkCommandInfo] -> Parser Command
|
||||||
commandParser cmds | (hidden, visible) <- partition isHidden cmds = asum
|
commandParser cmds | (hidden, visible) <- partition isHidden cmds = asum
|
||||||
@ -104,7 +128,7 @@ readSdkVersion =
|
|||||||
eitherReader (mapLeft displayException . parseVersion . pack)
|
eitherReader (mapLeft displayException . parseVersion . pack)
|
||||||
|
|
||||||
-- | Completer that uses the builtin bash completion.
|
-- | Completer that uses the builtin bash completion.
|
||||||
-- We use this to ensure that `daml build -o foo` will still complete to `daml build -o foobar.dar`.
|
-- We use this as a fallback for commands that do not use optparse-applicative to at least get file completions.
|
||||||
defaultCompleter :: Completer
|
defaultCompleter :: Completer
|
||||||
defaultCompleter = mkCompleter $ \word -> do
|
defaultCompleter = mkCompleter $ \word -> do
|
||||||
-- The implementation here is a variant of optparse-applicative’s `bashCompleter`.
|
-- The implementation here is a variant of optparse-applicative’s `bashCompleter`.
|
||||||
|
@ -4,36 +4,45 @@ commands:
|
|||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Launch DAML Studio"
|
desc: "Launch DAML Studio"
|
||||||
args: ["studio"]
|
args: ["studio"]
|
||||||
|
completion: true
|
||||||
- name: new
|
- name: new
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Create a new DAML project"
|
desc: "Create a new DAML project"
|
||||||
args: ["new"]
|
args: ["new"]
|
||||||
|
completion: true
|
||||||
- name: migrate
|
- name: migrate
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
args: ["migrate"]
|
args: ["migrate"]
|
||||||
|
completion: true
|
||||||
- name: init
|
- name: init
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Configure a folder as a DAML project"
|
desc: "Configure a folder as a DAML project"
|
||||||
args: ["init"]
|
args: ["init"]
|
||||||
|
completion: true
|
||||||
- name: build
|
- name: build
|
||||||
path: damlc/damlc
|
path: damlc/damlc
|
||||||
args: ["build", "--project-check"]
|
args: ["build", "--project-check"]
|
||||||
desc: "Build the DAML project into a DAR file"
|
desc: "Build the DAML project into a DAR file"
|
||||||
|
completion: true
|
||||||
- name: test
|
- name: test
|
||||||
path: damlc/damlc
|
path: damlc/damlc
|
||||||
args: ["test"]
|
args: ["test"]
|
||||||
desc: "Run the scenarios in the given DAML file and all dependencies"
|
desc: "Run the scenarios in the given DAML file and all dependencies"
|
||||||
|
completion: true
|
||||||
- name: start
|
- name: start
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
args: ["start"]
|
args: ["start"]
|
||||||
desc: "Launch Sandbox and Navigator for current DAML project"
|
desc: "Launch Sandbox and Navigator for current DAML project"
|
||||||
|
completion: true
|
||||||
- name: clean
|
- name: clean
|
||||||
path: damlc/damlc
|
path: damlc/damlc
|
||||||
args: ["clean", "--project-check"]
|
args: ["clean", "--project-check"]
|
||||||
desc: "Delete build artifacts from project folder"
|
desc: "Delete build artifacts from project folder"
|
||||||
|
completion: true
|
||||||
- name: damlc
|
- name: damlc
|
||||||
path: damlc/damlc
|
path: damlc/damlc
|
||||||
desc: "Run the DAML compiler"
|
desc: "Run the DAML compiler"
|
||||||
|
completion: true
|
||||||
- name: sandbox
|
- name: sandbox
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Launch the Sandbox"
|
desc: "Launch the Sandbox"
|
||||||
@ -50,14 +59,17 @@ commands:
|
|||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Interact with a DAML ledger (experimental)"
|
desc: "Interact with a DAML ledger (experimental)"
|
||||||
args: ["ledger"]
|
args: ["ledger"]
|
||||||
|
completion: true
|
||||||
- name: codegen
|
- name: codegen
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Run a language bindings code generation tool"
|
desc: "Run a language bindings code generation tool"
|
||||||
args: ["codegen"]
|
args: ["codegen"]
|
||||||
|
completion: true
|
||||||
- name: deploy
|
- name: deploy
|
||||||
path: daml-helper/daml-helper
|
path: daml-helper/daml-helper
|
||||||
desc: "Deploy DAML project to a ledger (experimental)"
|
desc: "Deploy DAML project to a ledger (experimental)"
|
||||||
args: ["deploy"]
|
args: ["deploy"]
|
||||||
|
completion: true
|
||||||
- name: ide
|
- name: ide
|
||||||
path: damlc/damlc
|
path: damlc/damlc
|
||||||
args: ["lax", "ide"]
|
args: ["lax", "ide"]
|
||||||
|
Loading…
Reference in New Issue
Block a user