config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

lsp-pwsh.el (15256B)


      1 ;;; lsp-pwsh.el --- client for PowerShellEditorServices  -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2019  Kien Nguyen
      4 
      5 ;; Author: kien.n.quang at gmail.com
      6 ;; Keywords: lsp
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 
     23 ;;
     24 
     25 ;;; Code:
     26 
     27 (require 'f)
     28 (require 'dash)
     29 (require 's)
     30 (require 'ht)
     31 
     32 (require 'lsp-protocol)
     33 (require 'lsp-mode)
     34 
     35 (defgroup lsp-pwsh nil
     36   "LSP support for PowerShell, using the PowerShellEditorServices."
     37   :group 'lsp-mode
     38   :package-version '(lsp-mode . "6.2"))
     39 
     40 ;; PowerShell vscode flags
     41 (defcustom lsp-pwsh-help-completion "BlockComment"
     42   "Controls the comment-based help completion behavior triggered by typing '##'.
     43 Set the generated help style with 'BlockComment' or 'LineComment'.
     44 Disable the feature with 'Disabled'."
     45   :type
     46   '(choice
     47     (:tag "Disabled" "BlockComment" "LineComment"))
     48   :group 'lsp-pwsh
     49   :package-version '(lsp-mode . "6.2"))
     50 
     51 (defcustom lsp-pwsh-script-analysis-enable t
     52   "Enables real-time script analysis from PowerShell Script Analyzer.
     53 Uses the newest installed version of the PSScriptAnalyzer module or the
     54 version bundled with this extension, if it is newer."
     55   :type 'boolean
     56   :group 'lsp-pwsh
     57   :package-version '(lsp-mode . "6.2"))
     58 
     59 (defcustom lsp-pwsh-script-analysis-settings-path ""
     60   "Specifies the path to a PowerShell Script Analyzer settings file.
     61 To override the default settings for all projects, enter an absolute path,
     62 or enter a path relative to your workspace."
     63   :type 'string
     64   :group 'lsp-pwsh
     65   :package-version '(lsp-mode . "6.2"))
     66 
     67 (defcustom lsp-pwsh-code-folding-enable t
     68   "Enables syntax based code folding.
     69 When disabled, the default indentation based code folding is used."
     70   :type 'boolean
     71   :group 'lsp-pwsh
     72   :package-version '(lsp-mode . "6.2"))
     73 
     74 (defcustom lsp-pwsh-code-folding-show-last-line t
     75   "Shows the last line of a folded section.
     76 Similar to the default VSCode folding style.
     77 When disabled, the entire folded region is hidden."
     78   :type 'boolean
     79   :group 'lsp-pwsh
     80   :package-version '(lsp-mode . "6.2"))
     81 
     82 (defcustom lsp-pwsh-code-formatting-preset "Custom"
     83   "Sets the codeformatting options to follow the given indent style.
     84 Sets in a way that is compatible with PowerShell syntax.
     85 For more information about the brace styles please refer to https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81."
     86   :type
     87   '(choice
     88     (:tag "Custom" "Allman" "OTBS" "Stroustrup"))
     89   :group 'lsp-pwsh
     90   :package-version '(lsp-mode . "6.2"))
     91 
     92 (defcustom lsp-pwsh-code-formatting-open-brace-on-same-line t
     93   "Places open brace on the same line as its associated statement."
     94   :type 'boolean
     95   :group 'lsp-pwsh
     96   :package-version '(lsp-mode . "6.2"))
     97 
     98 (defcustom lsp-pwsh-code-formatting-new-line-after-open-brace t
     99   "Adds a newline (line break) after an open brace."
    100   :type 'boolean
    101   :group 'lsp-pwsh
    102   :package-version '(lsp-mode . "6.2"))
    103 
    104 (defcustom lsp-pwsh-code-formatting-new-line-after-close-brace t
    105   "Adds a newline (line break) after a closing brace."
    106   :type 'boolean
    107   :group 'lsp-pwsh
    108   :package-version '(lsp-mode . "6.2"))
    109 
    110 (defcustom lsp-pwsh-code-formatting-pipeline-indentation-style "NoIndentation"
    111   "Multi-line pipeline style settings."
    112   :type
    113   '(choice
    114     (:tag "IncreaseIndentationForFirstPipeline" "IncreaseIndentationAfterEveryPipeline" "NoIndentation"))
    115   :group 'lsp-pwsh
    116   :package-version '(lsp-mode . "6.2"))
    117 
    118 (defcustom lsp-pwsh-code-formatting-whitespace-before-open-brace t
    119   "Adds a space between a keyword and its associated scriptblock expression."
    120   :type 'boolean
    121   :group 'lsp-pwsh
    122   :package-version '(lsp-mode . "6.2"))
    123 
    124 (defcustom lsp-pwsh-code-formatting-whitespace-before-open-paren t
    125   "Adds a space between a keyword (if, elseif, while, switch, etc) and its
    126 associated conditional expression."
    127   :type 'boolean
    128   :group 'lsp-pwsh
    129   :package-version '(lsp-mode . "6.2"))
    130 
    131 (defcustom lsp-pwsh-code-formatting-whitespace-around-operator t
    132   "Adds spaces before and after an operator ('=', '+', '-', etc.)."
    133   :type 'boolean
    134   :group 'lsp-pwsh
    135   :package-version '(lsp-mode . "6.2"))
    136 
    137 (defcustom lsp-pwsh-code-formatting-whitespace-after-separator t
    138   "Adds a space after a separator (',' and ';')."
    139   :type 'boolean
    140   :group 'lsp-pwsh
    141   :package-version '(lsp-mode . "6.2"))
    142 
    143 (defcustom lsp-pwsh-code-formatting-whitespace-inside-brace t
    144   "Adds a space after an opening brace ('{') and before a closing brace ('}')."
    145   :type 'boolean
    146   :group 'lsp-pwsh
    147   :package-version '(lsp-mode . "6.2"))
    148 
    149 (defcustom lsp-pwsh-code-formatting-whitespace-around-pipe t
    150   "Adds a space before and after the pipeline operator ('|')."
    151   :type 'boolean
    152   :group 'lsp-pwsh
    153   :package-version '(lsp-mode . "6.2"))
    154 
    155 (defcustom lsp-pwsh-code-formatting-ignore-one-line-block t
    156   "Does not reformat one-line code blocks, such as \"if (...) {...} else
    157 {...}\"."
    158   :type 'boolean
    159   :group 'lsp-pwsh
    160   :package-version '(lsp-mode . "6.2"))
    161 
    162 (defcustom lsp-pwsh-code-formatting-align-property-value-pairs t
    163   "Align assignment statements in a hashtable or a DSC Configuration."
    164   :type 'boolean
    165   :group 'lsp-pwsh
    166   :package-version '(lsp-mode . "6.2"))
    167 
    168 (defcustom lsp-pwsh-code-formatting-use-correct-casing nil
    169   "Use correct casing for cmdlets."
    170   :type 'boolean
    171   :group 'lsp-pwsh
    172   :package-version '(lsp-mode . "6.2"))
    173 
    174 (defcustom lsp-pwsh-developer-editor-services-log-level "Normal"
    175   "Sets the log level for the PowerShell Editor Services host executable.
    176 Valid values are 'Diagnostic', 'Verbose', 'Normal', 'Warning', and 'Error'"
    177   :type
    178   '(choice
    179     (:tag "Diagnostic" "Verbose" "Normal" "Warning" "Error"))
    180   :group 'lsp-pwsh
    181   :package-version '(lsp-mode . "6.2"))
    182 
    183 (defcustom lsp-pwsh-developer-editor-services-wait-for-debugger nil
    184   "Launches the language service with the /waitForDebugger flag to force it to
    185 wait for a .NET debugger to attach before proceeding."
    186   :type 'boolean
    187   :group 'lsp-pwsh
    188   :package-version '(lsp-mode . "6.2"))
    189 
    190 (defcustom lsp-pwsh-developer-feature-flags nil
    191   "An array of strings that enable experimental features in the PowerShell
    192 extension."
    193   :type
    194   '(repeat string)
    195   :group 'lsp-pwsh
    196   :package-version '(lsp-mode . "6.2"))
    197 
    198 (lsp-register-custom-settings
    199  '(("powershell.developer.featureFlags" lsp-pwsh-developer-feature-flags)
    200    ("powershell.developer.editorServicesWaitForDebugger" lsp-pwsh-developer-editor-services-wait-for-debugger t)
    201    ("powershell.codeFormatting.useCorrectCasing" lsp-pwsh-code-formatting-use-correct-casing t)
    202    ("powershell.codeFormatting.alignPropertyValuePairs" lsp-pwsh-code-formatting-align-property-value-pairs t)
    203    ("powershell.codeFormatting.ignoreOneLineBlock" lsp-pwsh-code-formatting-ignore-one-line-block t)
    204    ("powershell.codeFormatting.whitespaceAroundPipe" lsp-pwsh-code-formatting-whitespace-around-pipe t)
    205    ("powershell.codeFormatting.whitespaceInsideBrace" lsp-pwsh-code-formatting-whitespace-inside-brace t)
    206    ("powershell.codeFormatting.whitespaceAfterSeparator" lsp-pwsh-code-formatting-whitespace-after-separator t)
    207    ("powershell.codeFormatting.whitespaceAroundOperator" lsp-pwsh-code-formatting-whitespace-around-operator t)
    208    ("powershell.codeFormatting.whitespaceBeforeOpenParen" lsp-pwsh-code-formatting-whitespace-before-open-paren t)
    209    ("powershell.codeFormatting.whitespaceBeforeOpenBrace" lsp-pwsh-code-formatting-whitespace-before-open-brace t)
    210    ("powershell.codeFormatting.pipelineIndentationStyle" lsp-pwsh-code-formatting-pipeline-indentation-style)
    211    ("powershell.codeFormatting.newLineAfterCloseBrace" lsp-pwsh-code-formatting-new-line-after-close-brace t)
    212    ("powershell.codeFormatting.newLineAfterOpenBrace" lsp-pwsh-code-formatting-new-line-after-open-brace t)
    213    ("powershell.codeFormatting.openBraceOnSameLine" lsp-pwsh-code-formatting-open-brace-on-same-line t)
    214    ("powershell.codeFormatting.preset" lsp-pwsh-code-formatting-preset)
    215    ("powershell.codeFolding.showLastLine" lsp-pwsh-code-folding-show-last-line t)
    216    ("powershell.codeFolding.enable" lsp-pwsh-code-folding-enable t)
    217    ("powershell.scriptAnalysis.settingsPath" lsp-pwsh-script-analysis-settings-path)
    218    ("powershell.scriptAnalysis.enable" lsp-pwsh-script-analysis-enable t)
    219    ("powershell.helpCompletion" lsp-pwsh-help-completion)))
    220 
    221 ;; lsp-pwsh custom variables
    222 (defcustom lsp-pwsh-ext-path (expand-file-name "pwsh" lsp-server-install-dir)
    223   "The path to powershell vscode extension."
    224   :type 'string
    225   :group 'lsp-pwsh
    226   :package-version '(lsp-mode . "6.2"))
    227 
    228 (defcustom lsp-pwsh-exe (or (executable-find "pwsh") (executable-find "powershell"))
    229   "PowerShell executable."
    230   :type 'string
    231   :group 'lsp-pwsh
    232   :package-version '(lsp-mode . "6.2"))
    233 
    234 (defcustom lsp-pwsh-dir lsp-pwsh-ext-path
    235   "Path to PowerShellEditorServices without last slash."
    236   :type 'string
    237   :group 'lsp-pwsh
    238   :package-version '(lsp-mode . "6.2"))
    239 
    240 (defvar lsp-pwsh-pses-script (expand-file-name "PowerShellEditorServices/Start-EditorServices.ps1"
    241                                                lsp-pwsh-dir)
    242   "Main script to start PSES.")
    243 
    244 (defvar lsp-pwsh-log-path (expand-file-name "logs" lsp-pwsh-ext-path)
    245   "Path to directory where server will write log files.
    246 Must not nil.")
    247 
    248 (defvar lsp-pwsh--sess-id (emacs-pid))
    249 
    250 (defun lsp-pwsh--command ()
    251   "Return the command to start server."
    252   `(,lsp-pwsh-exe "-NoProfile" "-NonInteractive" "-NoLogo"
    253                   ,@(if (eq system-type 'windows-nt) '("-ExecutionPolicy" "Bypass"))
    254                   "-OutputFormat" "Text"
    255                   "-File"
    256                   ,lsp-pwsh-pses-script
    257                   "-HostName" "\"Emacs Host\""
    258                   "-HostProfileId" "'Emacs.LSP'"
    259                   "-HostVersion" "9.0.0"
    260                   "-LogPath" ,(expand-file-name "emacs-powershell.log" lsp-pwsh-log-path)
    261                   "-LogLevel" ,lsp-pwsh-developer-editor-services-log-level
    262                   "-SessionDetailsPath" ,(expand-file-name (format "PSES-VSCode-%d" lsp-pwsh--sess-id)
    263                                                            lsp-pwsh-log-path)
    264                   ;; "-AdditionalModules" "@('PowerShellEditorServices.VSCode')"
    265                   "-Stdio"
    266                   "-BundledModulesPath" ,lsp-pwsh-dir
    267                   "-FeatureFlags" "@()"))
    268 
    269 (defun lsp-pwsh--extra-init-params ()
    270   "Return form describing parameters for language server.")
    271 
    272 (lsp-defun lsp-pwsh--apply-code-action-edits ((&Command :command :arguments?))
    273   "Handle ACTION for PowerShell.ApplyCodeActionEdits."
    274   (-if-let* (((&pwsh:ScriptRegion :start-line-number :end-line-number
    275                                   :start-column-number :end-column-number :text)
    276               (lsp-seq-first arguments?))
    277              (start-position (lsp-make-position :line (1- start-line-number)
    278                                                 :character (1- start-column-number)))
    279              (end-position (lsp-make-position :line (1- end-line-number)
    280                                               :character (1- end-column-number)))
    281              (edits `[,(lsp-make-text-edit :range (lsp-make-range :start start-position
    282                                                                   :end end-position)
    283                                            :newText text)]))
    284       (lsp--apply-text-edits edits 'code-action)
    285     (lsp-send-execute-command command arguments?)))
    286 
    287 (lsp-defun lsp-pwsh--show-code-action-document ((&Command :arguments?))
    288   "Handle ACTION for PowerShell.ShowCodeActionDocumentation."
    289   (-if-let* ((rule-raw (lsp-seq-first arguments?))
    290              (rule-id (if (s-prefix-p "PS" rule-raw) (substring rule-raw 2) rule-raw)))
    291       (browse-url
    292        (concat "https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/rules/"
    293                rule-id))
    294     (lsp-warn "Cannot show documentation for code action, no ruleName was supplied")))
    295 
    296 (lsp-register-client
    297  (make-lsp-client
    298   :new-connection (lsp-stdio-connection #'lsp-pwsh--command
    299                                         (lambda ()
    300                                           (f-exists? lsp-pwsh-pses-script)))
    301   :activation-fn (lsp-activate-on "powershell")
    302   :server-id 'pwsh-ls
    303   :priority -1
    304   :initialization-options #'lsp-pwsh--extra-init-params
    305   :notification-handlers (ht ("powerShell/executionStatusChanged" #'ignore)
    306                              ("output" #'ignore))
    307   :action-handlers (ht ("PowerShell.ApplyCodeActionEdits"
    308                         #'lsp-pwsh--apply-code-action-edits)
    309                        ("PowerShell.ShowCodeActionDocumentation"
    310                         #'lsp-pwsh--show-code-action-document))
    311   :initialized-fn (lambda (w)
    312                     (with-lsp-workspace w
    313                       (lsp--set-configuration
    314                        (lsp-configuration-section "powershell")))
    315                     (let ((caps (lsp--workspace-server-capabilities w)))
    316                       (lsp:set-server-capabilities-document-range-formatting-provider? caps t)
    317                       (lsp:set-server-capabilities-document-formatting-provider? caps t)))
    318   :download-server-fn #'lsp-pwsh-setup))
    319 
    320 (defcustom lsp-pwsh-github-asset-url
    321   "https://github.com/%s/%s/releases/latest/download/%s"
    322   "GitHub latest asset template url."
    323   :type 'string
    324   :group 'lsp-pwsh
    325   :package-version '(lsp-mode . "6.2"))
    326 
    327 (defun lsp-pwsh-setup (_client callback error-callback update)
    328   "Downloads PowerShellEditorServices to `lsp-pwsh-dir'.
    329 CALLBACK is called when the download finish successfully otherwise
    330 ERROR-CALLBACK is called.
    331 UPDATE is non-nil if it is already downloaded.
    332 FORCED if specified with prefix argument."
    333 
    334   (unless (and lsp-pwsh-exe (file-executable-p lsp-pwsh-exe))
    335     (user-error "Use `lsp-pwsh-exe' with the value of `%s' is not a valid powershell binary"
    336                 lsp-pwsh-exe))
    337 
    338   (let ((url (format lsp-pwsh-github-asset-url "PowerShell"
    339                      "PowerShellEditorServices" "PowerShellEditorServices.zip"))
    340         (temp-file (make-temp-file "ext" nil ".zip")))
    341     (unless (f-exists? lsp-pwsh-log-path)
    342       (mkdir lsp-pwsh-log-path 'create-parent))
    343     (unless (and (not update) (f-exists? lsp-pwsh-pses-script))
    344       ;; since we know it's installed, use powershell to download the file
    345       ;; (and avoid url.el bugginess or additional libraries)
    346       (when (f-exists? lsp-pwsh-dir) (delete-directory lsp-pwsh-dir 'recursive))
    347       (lsp-async-start-process
    348        callback
    349        error-callback
    350        lsp-pwsh-exe "-noprofile" "-noninteractive" "-nologo"
    351        "-ex" "bypass" "-command"
    352        "Invoke-WebRequest" "-UseBasicParsing" "-uri" url "-outfile" temp-file ";"
    353        "Expand-Archive" "-Path" temp-file
    354        "-DestinationPath" lsp-pwsh-dir))))
    355 
    356 (lsp-consistency-check lsp-pwsh)
    357 
    358 (provide 'lsp-pwsh)
    359 ;;; lsp-pwsh.el ends here