4

I try to set proper completions/suggestions for Javascript. There is no problem with ES6+ and Typescript types and defined variables but most of the time I work with vanilla JS in browser context, so I need suggestions for standard JS WebAPI like for document (getElementById, addEventListener, querySelector etc.), Element properties and methods (classList, parentNode etc.), Math, and other variables and types available from window object. Currently I use company-mode, web-mode, lsp-mode, company-lsp.

I tried some options that didn't work. Most of the official tutorials didn't address this issue and often relate to package that are abandoned.

The preferred solution would involve LSP because of it's flexibility to work with many languages, but I'm open for other working suggestions.

Should I install any npm packages, typings for Typescript to configure it properly with tsconfig.json or I don't need that at all? If possible I would like to avoid Typescript when it's not needed.

1
  • Like your idea. Though I don't use LSP, I can figure out another solution.
    – chen bin
    Commented Dec 14, 2020 at 0:12

4 Answers 4

2

This solution is not using LSP. It only uses Ctags. You could use company-mode's default backend company-etags or 3rd party package company-ctags for code completion.

The key question is how to generate TAGS which is read by company backend.

I use my own package counsel-etags (https://github.com/redguardtoo/counsel-etags) to generate TAGS automatically. It provides a hook and some helper functions to append extra content into TAGS after it's generated by ctags.

The killer-feature is open the vanilla javascript API's online documentation when M-x counsel-etags-find-tag-at-point.

Screeshot, enter image description here

Set up,

(defvar my-js-document-tagnames
  '(characterSet
    childElementCount
    children
    compatMode
    contentType
    currentScript
    defaultView
    designMode
    doctype
    documentElement
    documentURI
    documentURIObject
    domain
    domConfig
    embeds
    fgColor
    firstElementChild
    forms
    fullscreen
    fullscreenEnabled
    height
    hidden
    images
    implementation
    lastElementChild
    lastModified
    lastStyleSheetSet
    linkColor
    links
    location
    mozSyntheticDocument
    onabort
    onafterscriptexecute
    onanimationcancel
    onanimationend
    onanimationiteration
    onauxclick
    onbeforescriptexecute
    onblur
    oncancel
    oncanplay
    oncanplaythrough
    onchange
    onclick
    onclose
    oncontextmenu
    oncuechange
    ondblclick
    ondurationchange
    onended
    onerror
    onfocus
    onformdata
    onfullscreenchange
    onfullscreenerror
    ongotpointercapture
    oninput
    oninvalid
    onkeydown
    onkeypress
    onkeyup
    onload
    onloadeddata
    onloadedmetadata
    onloadend
    onloadstart
    onlostpointercapture
    onmousedown
    onmouseenter
    onmouseleave
    onmousemove
    onmouseout
    onmouseover
    onmouseup
    onoffline
    ononline
    onpause
    onplay
    onplaying
    onpointercancel
    onpointerdown
    onpointerenter
    onpointerleave
    onpointermove
    onpointerout
    onpointerover
    onpointerup
    onreset
    onresize
    onscroll
    onselect
    onselectionchange
    onselectstart
    onsubmit
    ontouchcancel
    ontouchstart
    ontransitioncancel
    ontransitionend
    onvisibilitychange
    onwheel
    origin
    pictureInPictureEnabled
    plugins
    popupNode
    preferredStyleSheetSet
    readyState
    referrer
    rootElement
    scripts
    scrollingElement
    selectedStyleSheetSet
    styleSheetSets
    timeline
    title
    tooltipNode
    visibilityState
    vlinkColor
    width
    xmlEncoding
    xmlVersion
    adoptNode
    append
    caretRangeFromPoint
    clear
    close
    createAttribute
    createCDATASection
    createComment
    createDocumentFragment
    createElement
    createElementNS
    createEntityReference
    createEvent
    createExpression
    createExpression
    createNodeIterator
    createNSResolver
    createNSResolver
    createProcessingInstruction
    createRange
    createTextNode
    createTouch
    createTouchList
    createTreeWalker
    enableStyleSheetsForSet
    evaluate
    evaluate
    execCommand
    exitFullscreen
    exitPictureInPicture
    exitPointerLock
    getBoxObjectFor
    getElementById
    getElementsByClassName
    getElementsByName
    getElementsByTagName
    getElementsByTagNameNS
    hasFocus
    hasStorageAccess
    importNode
    mozSetImageElement
    prepend
    queryCommandEnabled
    queryCommandSupported
    querySelector
    querySelector
    querySelectorAll
    querySelectorAll
    registerElement
    releaseCapture
    replaceChildren
    requestStorageAccess
    write
    writeln))

(defvar my-js-window-tagnames
  '(caches
    closed
    console
    controllers
    crossOriginIsolated
    crypto
    customElements
    defaultStatus
    devicePixelRatio
    dialogArguments
    directories
    document
    event
    frameElement
    frames
    fullScreen
    history
    indexedDB
    innerHeight
    innerWidth
    isSecureContext
    isSecureContext
    length
    localStorage
    location
    locationbar
    menubar
    mozAnimationStartTime
    mozInnerScreenX
    mozInnerScreenY
    mozPaintCount
    name
    navigator
    onabort
    onafterprint
    onanimationcancel
    onanimationend
    onanimationiteration
    onappinstalled
    onauxclick
    onbeforeinstallprompt
    onbeforeprint
    onbeforeunload
    onblur
    oncancel
    oncanplay
    oncanplaythrough
    onchange
    onclick
    onclose
    oncontextmenu
    oncuechange
    ondblclick
    ondevicelight
    ondevicemotion
    ondeviceorientation
    ondeviceorientationabsolute
    ondeviceproximity
    ondragdrop
    ondurationchange
    onended
    onerror
    onfocus
    onformdata
    ongamepadconnected
    ongamepaddisconnected
    ongotpointercapture
    onhashchange
    oninput
    oninvalid
    onkeydown
    onkeypress
    onkeyup
    onlanguagechange
    onload
    onloadeddata
    onloadedmetadata
    onloadend
    onloadstart
    onlostpointercapture
    onmessage
    onmessageerror
    onmousedown
    onmouseenter
    onmouseleave
    onmousemove
    onmouseout
    onmouseover
    onmouseup
    onmozbeforepaint
    onpaint
    onpause
    onplay
    onplaying
    onpointercancel
    onpointerdown
    onpointerenter
    onpointerleave
    onpointermove
    onpointerout
    onpointerover
    onpointerup
    onpopstate
    onrejectionhandled
    onreset
    onresize
    onscroll
    onselect
    onselectionchange
    onselectstart
    onstorage
    onsubmit
    ontouchcancel
    ontouchstart
    ontransitioncancel
    ontransitionend
    onunhandledrejection
    onunload
    onuserproximity
    onvrdisplayactivate
    onvrdisplayblur
    onvrdisplayconnect
    onvrdisplaydeactivate
    onvrdisplaydisconnect
    onvrdisplayfocus
    onvrdisplaypointerrestricted
    onvrdisplaypointerunrestricted
    onvrdisplaypresentchange
    onwheel
    opener
    origin
    outerHeight
    outerWidth
    pageXOffset
    pageYOffset
    parent
    performance
    personalbar
    pkcs11
    screen
    screenLeft
    screenTop
    screenX
    screenY
    scrollbars
    scrollMaxX
    scrollMaxY
    scrollX
    scrollY
    self
    sessionStorage
    sidebar
    speechSynthesis
    status
    statusbar
    toolbar
    top
    visualViewport
    window

    Methods

    alert
    atob
    back
    blur
    btoa
    cancelAnimationFrame
    cancelIdleCallback
    captureEvents
    clearImmediate
    clearInterval
    clearTimeout
    close
    confirm
    convertPointFromNodeToPage
    convertPointFromPageToNode
    createImageBitmap
    dump
    fetch
    find
    focus
    forward
    getAttention
    getComputedStyle
    getDefaultComputedStyle
    getSelection
    home
    matchMedia
    minimize
    moveBy
    moveTo
    open
    openDialog
    postMessage
    print
    prompt
    queueMicrotask
    releaseEvents
    requestAnimationFrame
    requestFileSystem
    requestIdleCallback
    resizeBy
    resizeTo
    routeEvent
    scroll
    scrollBy
    scrollByLines
    scrollByPages
    scrollTo
    setCursor
    setImmediate
    setInterval
    setTimeout
    showModalDialog
    sizeToContent
    stop
    updateCommands))

(defun my-update-tags-file (tags-file)
  "Update TAGS-FILE."
  (when (memq major-mode '(js-mode typescript-mode js2-mode))
    (let ((s1 (mapconcat (lambda (tagname) (counsel-etags-tag-line tagname tagname 0)) my-js-document-tagnames ""))
          (s2 (mapconcat (lambda (tagname) (counsel-etags-tag-line tagname tagname 0)) my-js-window-tagnames ""))
          (s3 (mapconcat (lambda (tagname) (counsel-etags-tag-line tagname tagname 0)) '(addEventListener
                                                                                         dispatchEvent
                                                                                         removeEventListener) "")))
      (counsel-etags-append-to-tags-file
       (list (cons "https://developer.mozilla.org/en-US/docs/Web/API/Document/%s" s1)
             (cons "https://developer.mozilla.org/en-US/docs/Web/API/Window/%s" s2)
             (cons "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/%s" s3))
       tags-file))))
(add-hook 'counsel-etags-after-update-tags-hook 'my-update-tags-file)
1
  • Thanks for sharing, it looks like nice fallback if I wouldn't manage to set LSP, eglot or anything else more robust.
    – Navidot
    Commented Dec 14, 2020 at 12:46
0

I was only able to get this to work in LSP after switching from company-lsp to company-capf. This works for either TS or JS. If memory serves, I didn't need to do anything special to get it to work other than ensure that my node paths were set correctly and to ensure that the [js|ts]config.json has dom under compilerOptions -> lib array. enter image description here

3
  • Yes, it's pretty straightforward when we follow the instructions, but it may easily break when we try to customize it further. I broken my setting on first attempt by setting lsp-enable-completion-at-point to nil. I wanted completions to show up only on demand, but they turn out to not show at all. Some of the variables has to remain unchanged, otherwise some of functionalities will get broken. This is design flaw and they should be declared constant instead of variables to not cause confusion they may be customized.
    – Navidot
    Commented Dec 15, 2020 at 5:31
  • I didn't have to set dom in compilerOptions > lib the settings from LSP React tutorial I mentioned in my post complete even DOM types.
    – Navidot
    Commented Dec 15, 2020 at 5:34
  • Thanks @AdamFaryna, I missed that you had posted the solution above.
    – Jeremy
    Commented Dec 15, 2020 at 16:40
0

I managed to get it working with a little help of LSP support. I followed the guide https://emacs-lsp.github.io/lsp-mode/tutorials/reactjs-tutorial/ but there was missing part with setting path to executables node and npm.

I fixed that with this setting for my macOS environment:

(setenv "PATH" (concat "~/.npm-packages/bin:" "/usr/local/bin:" (getenv "PATH")))
(setq exec-path `("~/.npm-packages/bin" "/usr/local/bin" ,exec-path))

Entire semi-minimal init.el:

(package-initialize)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(setq package-selected-packages '(lsp-mode yasnippet lsp-treemacs helm-lsp projectile hydra flycheck company avy which-key helm-xref dap-mode zenburn-theme json-mode))
(when (cl-find-if-not #'package-installed-p package-selected-packages)
  (package-refresh-contents)
  (mapc #'package-install package-selected-packages))
(load-theme 'zenburn t)
(helm-mode)
(require 'helm-xref)
(define-key global-map [remap find-file] #'helm-find-files)
(define-key global-map [remap execute-extended-command] #'helm-M-x)
(define-key global-map [remap switch-to-buffer] #'helm-mini)
(which-key-mode)
(add-hook 'prog-mode-hook #'lsp)
(setq gc-cons-threshold (* 100 1024 1024)
      read-process-output-max (* 1024 1024)
      company-idle-delay 0.0
      company-minimum-prefix-length 1
      create-lockfiles nil ;; lock files will kill `npm start'
      lsp-headerline-breadcrumb-enable t)

(setenv "PATH" (concat "~/.npm-packages/bin:" "/usr/local/bin:" (getenv "PATH")))
(setq exec-path `("~/.npm-packages/bin" "/usr/local/bin" ,exec-path))

(with-eval-after-load 'lsp-mode
  (require 'dap-chrome)
  (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration)
  (yas-global-mode))

To make it work with company-mode make sure you have company-capf in front of company-backend.

0

Always use typescript mode

(add-to-list 'auto-mode-alist '("\\.js\\'" . typescript-mode))
(add-to-list 'auto-mode-alist '("\\.mjs\\'" . typescript-mode))
(add-to-list 'auto-mode-alist '("\\.cjs\\'" . typescript-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.