config

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

lsp-php.el (18379B)


      1 ;;; lsp-php.el --- description -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2020 emacs-lsp maintainers
      4 
      5 ;; Author: emacs-lsp maintainers
      6 ;; Keywords: lsp, php
      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 ;; LSP Clients for the PHP Programming Language.
     24 
     25 ;;; Code:
     26 
     27 (require 'lsp-mode)
     28 (require 'lsp-protocol)
     29 
     30 ;; PHP Language Server
     31 (defgroup lsp-php nil
     32   "LSP support for PHP, using php-language-server."
     33   :link '(url-link "https://github.com/felixfbecker/php-language-server")
     34   :group 'lsp-mode)
     35 
     36 (defun lsp-php-get-composer-dir ()
     37   "Get composer home directory if possible."
     38   (if (executable-find "composer")
     39       (replace-regexp-in-string "\n$" "" (shell-command-to-string "composer config --global home"))
     40     "~/.composer"))
     41 
     42 (defcustom lsp-php-composer-dir nil
     43   "Home directory of composer."
     44   :group 'lsp-php
     45   :type 'string)
     46 
     47 (defcustom lsp-clients-php-server-command nil
     48   "Install directory for php-language-server."
     49   :group 'lsp-php
     50   :type '(repeat string))
     51 
     52 (defun lsp-php--create-connection ()
     53   "Create lsp connection."
     54   (lsp-stdio-connection
     55    (lambda ()
     56      (unless lsp-php-composer-dir
     57        (setq lsp-php-composer-dir (lsp-php-get-composer-dir)))
     58      (unless lsp-clients-php-server-command
     59        (setq lsp-clients-php-server-command
     60              `("php",
     61                (expand-file-name
     62                 (f-join lsp-php-composer-dir "vendor/felixfbecker/language-server/bin/php-language-server.php")))))
     63      lsp-clients-php-server-command)
     64    (lambda ()
     65      (if (and (cdr lsp-clients-php-server-command)
     66               (eq (string-match-p "php[0-9.]*\\'" (car lsp-clients-php-server-command)) 0))
     67          ;; Start with the php command and the list has more elems. Test the existence of the PHP script.
     68          (let ((php-file (nth 1 lsp-clients-php-server-command)))
     69            (or (file-exists-p php-file)
     70                (progn
     71                  (lsp-log "%s is not present." php-file)
     72                  nil)))
     73        t))))
     74 
     75 (lsp-register-client
     76  (make-lsp-client :new-connection (lsp-php--create-connection)
     77                   :activation-fn (lsp-activate-on "php")
     78                   :priority -3
     79                   :server-id 'php-ls))
     80 
     81 ;;; Intelephense
     82 (defgroup lsp-intelephense nil
     83   "LSP support for PHP, using Intelephense."
     84   :group 'lsp-mode
     85   :link '(url-link "https://github.com/bmewburn/vscode-intelephense")
     86   :package-version '(lsp-mode . "6.1"))
     87 
     88 (lsp-defcustom lsp-intelephense-php-version "9.0.0"
     89   "Minimum version of PHP to refer to. Affects code actions, diagnostic &
     90 completions."
     91   :type 'string
     92   :group 'lsp-intelephense
     93   :package-version '(lsp-mode . "6.1")
     94   :lsp-path "intelephense.environment.phpVersion")
     95 
     96 (lsp-defcustom lsp-intelephense-files-max-size 1000000
     97   "Maximum file size in bytes."
     98   :type 'number
     99   :group 'lsp-intelephense
    100   :package-version '(lsp-mode . "6.1")
    101   :lsp-path "intelephense-files.maxSize")
    102 
    103 (lsp-defcustom lsp-intelephense-files-associations
    104   ["*.php" "*.phtml"]
    105   "Configure glob patterns to make files available for language
    106 server features."
    107   :type '(repeat string)
    108   :group 'lsp-intelephense
    109   :package-version '(lsp-mode . "6.1")
    110   :lsp-path "intelephense.files.associations")
    111 
    112 (lsp-defcustom lsp-intelephense-files-exclude
    113   ["**/.git/**" "**/.svn/**" "**/.hg/**" "**/CVS/**" "**/.DS_Store/**"
    114    "**/node_modules/**" "**/bower_components/**" "**/vendor/**/{Test,test,Tests,tests}/**"]
    115   "Configure glob patterns to exclude certain files and folders
    116 from all language server features."
    117   :type '(repeat string)
    118   :group 'lsp-intelephense
    119   :package-version '(lsp-mode . "6.1")
    120   :lsp-path "intelephense.files.exclude")
    121 
    122 (lsp-defcustom lsp-intelephense-paths-include
    123   []
    124   "Configure additional paths outside workspace."
    125   :type 'lsp-string-vector
    126   :group 'lsp-intelephense
    127   :package-version '(lsp-mode . "8.1")
    128   :lsp-path "intelephense.environment.includePaths")
    129 
    130 (lsp-defcustom lsp-intelephense-stubs
    131   ["apache" "bcmath" "bz2" "calendar"
    132    "com_dotnet" "Core" "ctype" "curl" "date" "dba" "dom" "enchant"
    133    "exif" "fileinfo" "filter" "fpm" "ftp" "gd" "hash" "iconv" "imap" "interbase"
    134    "intl" "json" "ldap" "libxml" "mbstring" "mcrypt" "meta" "mssql" "mysqli"
    135    "oci8" "odbc" "openssl" "pcntl" "pcre" "PDO" "pdo_ibm" "pdo_mysql"
    136    "pdo_pgsql" "pdo_sqlite" "pgsql" "Phar" "posix" "pspell" "readline" "recode"
    137    "Reflection" "regex" "session" "shmop" "SimpleXML" "snmp" "soap" "sockets"
    138    "sodium" "SPL" "sqlite3" "standard" "superglobals" "sybase" "sysvmsg"
    139    "sysvsem" "sysvshm" "tidy" "tokenizer" "wddx" "xml" "xmlreader" "xmlrpc"
    140    "xmlwriter" "Zend OPcache" "zip" "zlib"]
    141   "Configure stub files for built in symbols and common
    142 extensions. The default setting includes PHP core and all
    143 bundled extensions."
    144   :type '(repeat string)
    145   :group 'lsp-intelephense
    146   :package-version '(lsp-mode . "6.1")
    147   :lsp-path "intelephense.stubs")
    148 
    149 (lsp-defcustom lsp-intelephense-completion-insert-use-declaration t
    150   "Use declarations will be automatically inserted for namespaced
    151 classes, traits, interfaces, functions, and constants."
    152   :type 'boolean
    153   :group 'lsp-intelephense
    154   :package-version '(lsp-mode . "6.1")
    155   :lsp-path "intelephense.completion.insertUseDeclaration")
    156 
    157 (lsp-defcustom lsp-intelephense-completion-fully-qualify-global-constants-and-functions nil
    158   "Global namespace constants and functions will be fully
    159 qualified (prefixed with a backslash)."
    160   :type 'boolean
    161   :group 'lsp-intelephense
    162   :package-version '(lsp-mode . "6.1")
    163   :lsp-path "intelephense.completion.fullyQualifyGlobalConstantsAndFunctions")
    164 
    165 (lsp-defcustom lsp-intelephense-completion-trigger-parameter-hints t
    166   "Method and function completions will include parentheses and
    167 trigger parameter hints."
    168   :type 'boolean
    169   :group 'lsp-intelephense
    170   :package-version '(lsp-mode . "6.2")
    171   :lsp-path "intelephense.completion.triggerParameterHints")
    172 
    173 (lsp-defcustom lsp-intelephense-completion-max-items 100
    174   "The maximum number of completion items returned per request."
    175   :type 'number
    176   :group 'lsp-intelephense
    177   :package-version '(lsp-mode . "6.2")
    178   :lsp-path "intelephense.completion.maxItems")
    179 
    180 (lsp-defcustom lsp-intelephense-format-enable t
    181   "Enables formatting."
    182   :type 'boolean
    183   :group 'lsp-intelephense
    184   :package-version '(lsp-mode . "6.1")
    185   :lsp-path "intelephense.format.enable")
    186 
    187 (lsp-defcustom lsp-intelephense-format-braces "psr12"
    188   "Formatting braces style. psr12, allman or k&r"
    189   :type 'string
    190   :group 'lsp-intelephense
    191   :package-version '(lsp-mode . "8.1")
    192   :lsp-path "intelephense.format.braces")
    193 
    194 (defcustom lsp-intelephense-licence-key nil
    195   "Enter your intelephense licence key here to access premium
    196 features."
    197   :type 'string
    198   :group 'lsp-intelephense
    199   :package-version '(lsp-mode . "6.2"))
    200 
    201 (lsp-defcustom lsp-intelephense-telemetry-enabled nil
    202   "Anonymous usage and crash data will be sent to Azure
    203 Application Insights."
    204   :type 'boolean
    205   :group 'lsp-intelephense
    206   :package-version '(lsp-mode . "6.2")
    207   :lsp-path "intelephense.telemetry.enabled")
    208 
    209 (lsp-defcustom lsp-intelephense-rename-exclude
    210   ["**/vendor/**"]
    211   "Glob patterns to exclude files and folders from having symbols
    212 renamed. Rename operation will fail if references and/or
    213 definitions are found in excluded files/folders."
    214   :type '(repeat string)
    215   :group 'lsp-intelephense
    216   :package-version '(lsp-mode . "6.2")
    217   :lsp-path "intelephense.rename.exclude")
    218 
    219 (lsp-defcustom lsp-intelephense-trace-server "off"
    220   "Traces the communication between VSCode and the intelephense
    221 language server."
    222   :type '(choice (:tag "off" "messages" "verbose"))
    223   :group 'lsp-intelephense
    224   :package-version '(lsp-mode . "6.1")
    225   :lsp-path "intelephense.trace.server")
    226 
    227 (defcustom lsp-intelephense-storage-path
    228   (expand-file-name (locate-user-emacs-file "lsp-cache"))
    229   "Optional absolute path to storage dir."
    230   :type 'directory
    231   :group 'lsp-intelephense
    232   :package-version '(lsp-mode . "6.1"))
    233 
    234 (defcustom lsp-intelephense-global-storage-path
    235   (expand-file-name (locate-user-emacs-file "intelephense"))
    236   "Optional absolute path to global storage dir."
    237   :type 'directory
    238   :group 'lsp-intelephense
    239   :package-version '(lsp-mode . "9.0.0"))
    240 
    241 (defcustom lsp-intelephense-clear-cache nil
    242   "Optional flag to clear server state."
    243   :type 'boolean
    244   :group 'lsp-intelephense
    245   :package-version '(lsp-mode . "6.2"))
    246 
    247 (defcustom lsp-intelephense-multi-root t
    248   "Flag to control if the server supports multi-root projects."
    249   :type 'boolean
    250   :group 'lsp-intelephense
    251   :package-version '(lsp-mode . "6.3"))
    252 
    253 (define-obsolete-variable-alias
    254   'lsp-clients-php-iph-server-command
    255   'lsp-intelephense-server-command
    256   "lsp-mode 6.1")
    257 
    258 (defcustom lsp-intelephense-server-command
    259   `("intelephense" "--stdio")
    260   "Command to start Intelephense."
    261   :type '(repeat string)
    262   :group 'lsp-intelephense
    263   :package-version '(lsp-mode . "6.1"))
    264 
    265 (lsp-dependency 'intelephense
    266                 '(:system "intelephense")
    267                 '(:npm :package "intelephense"
    268                        :path "intelephense"))
    269 
    270 (lsp-register-client
    271  (make-lsp-client :new-connection (lsp-stdio-connection
    272                                    (lambda ()
    273                                      `(,(or (executable-find
    274                                              (cl-first lsp-intelephense-server-command))
    275                                             (lsp-package-path 'intelephense))
    276                                        ,@(cl-rest lsp-intelephense-server-command))))
    277                   :activation-fn (lsp-activate-on "php")
    278                   :priority -1
    279                   :notification-handlers (ht ("indexingStarted" #'ignore)
    280                                              ("indexingEnded" #'ignore))
    281                   :initialization-options (lambda ()
    282                                             (list :storagePath lsp-intelephense-storage-path
    283                                                   :globalStoragePath lsp-intelephense-global-storage-path
    284                                                   :licenceKey lsp-intelephense-licence-key
    285                                                   :clearCache lsp-intelephense-clear-cache))
    286                   :multi-root lsp-intelephense-multi-root
    287                   :completion-in-comments? t
    288                   :server-id 'iph
    289                   :download-server-fn (lambda (_client callback error-callback _update?)
    290                                         (lsp-package-ensure 'intelephense
    291                                                             callback error-callback))
    292                   :synchronize-sections '("intelephense")))
    293 
    294 
    295 ;;; Serenata
    296 (defgroup lsp-serenata nil
    297   "LSP support for the PHP programming language, using serenata."
    298   :group 'lsp-mode
    299   :link '(url-link "https://gitlab.com/Serenata/Serenata")
    300   :package-version '(lsp-mode . "7.0"))
    301 
    302 (defcustom lsp-serenata-server-path
    303   "serenata.phar"
    304   "Path to the Serenata Language Server phar file.
    305 It can be downloaded from https://gitlab.com/Serenata/Serenata/-/releases."
    306   :group 'lsp-serenata
    307   :type 'file)
    308 
    309 (defcustom lsp-serenata-uris
    310   []
    311   "A list of folders to index for your project.
    312 This does not have to include the root of the project itself, in
    313 case you have need of an exotic configuration where the root of
    314 the project is at some location but your actual PHP code is
    315 somewhere else.  Note that if you are running Serenata in a
    316 container, you will have to ensure that these URI's are mapped
    317 inside it.  Avoid using file paths containing spaces. This is
    318 currently broken due to apparent PHP quirks.  By default, the
    319 value is taken from the lsp workspace location."
    320   :group 'lsp-serenata
    321   :type 'lsp-string-vector)
    322 
    323 (defcustom lsp-serenata-php-version
    324   7.3
    325   "Allows you to specify the PHP version your project is written in.
    326 At the moment this directive is still ignored, but it will
    327 influence functionality such as refactoring in the future, where
    328 older PHP versions may not support scalar type hints, which may
    329 then be omitted from places such as getters and setters."
    330   :group 'lsp-serenata
    331   :type 'number)
    332 
    333 (defcustom lsp-serenata-file-extensions
    334   ["php"]
    335   "List of file extensions (without dot) to process.
    336 Files that do not match this whitelist will be ignored during
    337 indexing.  Usually you'll want to set this to at least include
    338 php, as it is the most common PHP extension.  phpt is not
    339 included by default as it is often used to contain test code that
    340 is not directly part of the code.  Note that for existing
    341 projects, removing extensions will not not automatically prune
    342 files having them from the index if they are already present.
    343 Adding new ones will cause the files having them to be picked up
    344 on the next project initialization."
    345   :group 'lsp-serenata
    346   :type 'lsp-string-vector)
    347 
    348 (defcustom lsp-serenata-index-database-uri (lsp--path-to-uri (f-join  user-emacs-directory "index.sqlite"))
    349   "The location to store the index database.
    350 Note that, as the index database uses SQLite and WAL mode,
    351 additional files (usually two) may be generated and used in the
    352 same folder.  Note also that Serenata relies on the Doctrine DBAL
    353 library as well as the SQLite backends in PHP, which may not
    354 support non-file URI's, which may prevent you from using these."
    355   :group 'lsp-serenata
    356   :type 'file)
    357 
    358 (defcustom lsp-serenata-exclude-path-expressions ["/.+Test.php$/"]
    359   "One or more expressions of paths to ignore.
    360 This uses Symfony's Finder in the background, so this means you
    361 can configure anything here that can also be passed to the name
    362 function, which includes plain strings, globs, as well as regular
    363 expressions.  Note that for existing projects, modifying these
    364 will not not automatically prune them from the index if they are
    365 already present."
    366   :group 'lsp-serenata
    367   :type 'lsp-string-vector)
    368 
    369 (defun lsp-serenata-server-start-fun (port)
    370   "Define serenata start function, it requires a PORT."
    371   `(,lsp-serenata-server-path
    372     "-u" ,(number-to-string port)))
    373 
    374 (defun lsp-serenata-init-options ()
    375   "Init options for lsp-serenata."
    376   `( :configuration ( :uris ,lsp-serenata-uris
    377                       :indexDatabaseUri ,lsp-serenata-index-database-uri
    378                       :phpVersion ,lsp-serenata-php-version
    379                       :excludedPathExpressions ,lsp-serenata-exclude-path-expressions
    380                       :fileExtensions ,lsp-serenata-file-extensions)))
    381 
    382 
    383 (lsp-interface (serenata:didProgressIndexing (:sequenceOfIndexedItem :totalItemsToIndex :progressPercentage :folderUri :fileUri :info) nil ))
    384 
    385 (lsp-register-client
    386  (make-lsp-client
    387   :new-connection (lsp-tcp-connection 'lsp-serenata-server-start-fun)
    388   :activation-fn (lsp-activate-on "php")
    389   :priority -2
    390   :notification-handlers (ht ("serenata/didProgressIndexing"
    391                               (lambda (_server data)
    392                                 (lsp-log "%s" (lsp:serenata-did-progress-indexing-info data)))))
    393 
    394   :initialization-options #'lsp-serenata-init-options
    395   :initialized-fn (lambda (workspace)
    396                     (when (equal (length lsp-serenata-uris) 0)
    397                       (let* ((lsp-root (lsp--path-to-uri (lsp-workspace-root))))
    398                         (setq lsp-serenata-uris (vector lsp-root))))
    399                     (with-lsp-workspace workspace
    400                       (lsp--set-configuration
    401                        (lsp-configuration-section "serenata"))))
    402   :server-id 'serenata))
    403 
    404 ;;; phpactor
    405 
    406 (defgroup lsp-phpactor nil
    407   "LSP support for Phpactor."
    408   :link '(url-link "https://github.com/phpactor/phpactor")
    409   :group 'lsp-mode)
    410 
    411 (defcustom lsp-phpactor-path nil
    412   "Path to the `phpactor' command."
    413   :group 'lsp-phpactor
    414   :type 'string)
    415 
    416 (lsp-register-client
    417  (make-lsp-client
    418   :new-connection (lsp-stdio-connection
    419                    (lambda ()
    420                      (unless lsp-php-composer-dir
    421                        (setq lsp-php-composer-dir (lsp-php-get-composer-dir)))
    422                      (unless lsp-phpactor-path
    423                        (setq lsp-phpactor-path (or (executable-find "phpactor")
    424                                                    (f-join lsp-php-composer-dir "vendor/phpactor/phpactor/bin/phpactor"))))
    425                      (list lsp-phpactor-path "language-server")))
    426   :activation-fn (lsp-activate-on "php")
    427   ;; `phpactor' is not really that feature-complete: it doesn't support
    428   ;; `textDocument/showOccurence' and sometimes errors (e.g. find references on
    429   ;; a global free-standing function).
    430   :priority -4
    431   ;; Even though `phpactor' itself supports no options, this needs to be
    432   ;; serialized as an empty object (otherwise the LS won't even start, due to a
    433   ;; type error).
    434   :initialization-options (ht)
    435   :server-id 'phpactor))
    436 
    437 (defcustom lsp-phpactor-extension-alist '(("Phpstan" . "phpactor/language-server-phpstan-extension")
    438                                           ("Behat" . "phpactor/behat-extension")
    439                                           ("PHPUnit" . "phpactor/phpunit-extension"))
    440   "Alist mapping extension names to `composer' packages.
    441 These extensions can be installed using
    442 `lsp-phpactor-install-extension'."
    443   :type '(alist :key-type "string" :value-type "string")
    444   :group 'lsp-phpactor)
    445 
    446 (defun lsp-phpactor-install-extension (extension)
    447   "Install a `phpactor' EXTENSION.
    448 See `lsp-phpactor-extension-alist' and
    449 https://phpactor.readthedocs.io/en/develop/extensions.html."
    450   (interactive (list (completing-read "Select extension: "
    451                                       lsp-phpactor-extension-alist)))
    452   (compilation-start
    453    (format "%s extension:install %s"
    454            (shell-quote-argument (expand-file-name lsp-phpactor-path))
    455            (shell-quote-argument
    456             (cdr (assoc extension lsp-phpactor-extension-alist))))
    457    nil
    458    (lambda (_mode)
    459      (format "*Phpactor install %s*" extension))))
    460 
    461 (lsp-consistency-check lsp-php)
    462 
    463 (provide 'lsp-php)
    464 ;;; lsp-php.el ends here