PSReadLine icon indicating copy to clipboard operation
PSReadLine copied to clipboard

Support non-script based file configuration of options

Open imacks opened this issue 7 years ago • 12 comments

Relying on profile.ps1 may not fit scenarios where we want to set-executionpolicy to ban scripts.

Would it be possible to populate options from the registry or json file? I would image modifying PSConsoleReadlineOptions is all that it takes?

imacks avatar Feb 21 '18 19:02 imacks

I'm definitely open to the idea.

Now that PSReadLine is portable to Mac/Linux, we should avoid the registry.

If this is implemented, my priorities are:

  • Be portable between:
    • Windows/Non-Windows
    • Full CLR/Core CLR
  • Avoid new dependencies, e.g. don't require shipping Newtonsoft.Json
    • Helps avoid introducing versioning problems w/ popular nuget packages
    • Helps simplify bringing this feature back into Windows

I think this rules out json and probably yaml (writing yet another parser is usually not a good idea.)

When I originally considered the problem of configuration, I settled on script because I hated xml. I still hate xml, but it might be the simplest option.

Maybe a better (but more complicated option) - use script, but don't execute it, instead interpret the Ast. You couldn't use arbitrary script, e.g. you could only call Set-PSReadLIneOption and Set-PSReadLineKeyHandler, pass only constants, no variables except perhaps reading environment variables, etc. It would be similar to the PowerShell data language (psd1 files) - so maybe we would just check for the file PSReadLineConfig.psd1 in the same directory as the user profile or something like that.

lzybkr avatar Feb 21 '18 20:02 lzybkr

wow that sounds great. I am happy to work on this feature if you like

imacks avatar Feb 22 '18 03:02 imacks

Or we can use a real script PSReadLine.profile.ps1 and "invoke" it by reading, creating a script block and invoking it. This bypasses the execution policy.

nightroman avatar Feb 22 '18 03:02 nightroman

Invoking wouldn't work in restricted language mode.

lzybkr avatar Feb 22 '18 06:02 lzybkr

I don't know. Why insist on a script when all we want to do is something simple? For my case, I just customized HKCU:\Console\ColorTable## to solarized and can't see anything dark gray with the default PSReadLine options. All I want to do is set parameter color to something that is not dark gray!

imacks avatar Feb 22 '18 08:02 imacks

I thought OP was talking about execution policy. It does not imply restricted language mode, it just does not allow invoke script directly. But they still may be invoked in many ways.

nightroman avatar Feb 22 '18 08:02 nightroman

@imacks The script approach has some advantages. Personally, I would prefer it regardless of the execution policy, at least to make my usual profile cleaner.

nightroman avatar Feb 22 '18 08:02 nightroman

@nightroman i don’t see a problem with script or xml or whatnot, so long as we can keep it as a simple way to change the defaults. I mean, the profile is always there.

imacks avatar Feb 22 '18 11:02 imacks

@nightroman - PSReadLine has extra security concerns that other modules might not when it ships with Windows, so I need to avoid exposing potentially exploitable holes.

That said, there is a way to invoke the script without violating the machine lockdown policy.

@imacks - thanks for offering to implement this. I think the script solution is better than xml because:

  • it should be less code
  • script blocks in xml can be painful if you need to escape anything
  • nobody wants xml :)

We'll need code similar to the following to invoke the configuration script block and not create a security hole.

var origLanguageMode = runspace.LanguageMode;
try {
    if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce)
    {
        runspace.LanguageMode = PSLanguageMode.ConstrainedLanguage;
    }
    scriptBlock.Invoke();
} finally {
    runspace.LanguageMode  = origLanguageMode;
}

lzybkr avatar Feb 22 '18 22:02 lzybkr

So... it is a file. What options are in there?

Here's what I can think of (convertfrom-stringdata styled capable of handling env variables)

EditMode = default
ContinuationPrompt = >>>
ContinuationPromptColor = green
 ExtraPromptLineCount = default
 #  AddToHistoryHandler = null
 HistoryNoDuplicates = default
            MaximumHistoryCount = default
            MaximumKillRingCount = default
            HistorySearchCursorMovesToEnd = default
            ShowToolTips = true
            DingDuration = 15
            DingTone = default
            BellStyle = default
            CompletionQueryItems = default
            WordDelimiters = default
            HistorySearchCaseSensitive = default
            HistorySaveStyle = default
            AnsiEscapeTimeout = default
   HistorySavePath = ${env:userprofile}/.history.log

CommentColor   =DarkGreen
KeywordColor   = Green
    StringColor    = DarkCyan
       OperatorColor  = DarkGray
       VariableColor  = Green
      CommandColor   = Yellow
     ParameterColor = DarkGray
        TypeColor      = Gray
      NumberColor    = White
       MemberColor    = Gray
        EmphasisColor  = Cyan
        ErrorColor  = Red

imacks avatar Feb 23 '18 03:02 imacks

Oh, @nightroman was just suggesting you still use a ps1 file like you would today, but PSReadLine would invoke it in a way that is not affected by the execution policy.

I use the following pattern in my profile to make it feel more like a configuration and less like calling a bunch of commands:

$options =
@{
    PromptText = '> '
    AddToHistoryHandler = {
        param([string]$line)
        return $line.Length -gt 3 -and $line[0] -ne ' ' -and $line[0] -ne ';'
    }
    HistorySavePath = "${env:HOMEDRIVE}${env:HOMEPATH}/PSReadLine_history.txt"
    EditMode = 'Emacs'
    ExtraPromptLineCount = 1
    Colors = @{
        Error = "$([char](0x1b))[44;91m"
    }
}
Set-PSReadLineOption @options

I suppose one option is to mimic the module manifest, basically remove the first and last lines in my example above, then apply the options. This would also need to allow specifying key bindings, maybe like:

@{
    KeyBindings = @{
        UpArrow = 'HistorySearchBackward'
        DownArrow = 'HistorySearchForward'
        'Alt+F1' = @{
            BriefDescription = 'CommandHelp'
            LongDescription = 'Open the help window for the current command'
            ScriptBlock = { ... }
        }
    }
}

lzybkr avatar Feb 23 '18 03:02 lzybkr

There's precedent for this kind of configuration data in .psd1 files with DSC as well: https://docs.microsoft.com/en-us/powershell/dsc/separatingenvdata

bgshacklett avatar Feb 23 '18 06:02 bgshacklett