css-mode documentation lookup feature
* etc/NEWS: Mention new feature. * lisp/textmodes/css-mode.el (css-mode-map): New defvar. (css--mdn-lookup-history): New defvar. (css-lookup-url-format): New defcustom. (css--mdn-property-regexp, css--mdn-completion-list): New defconsts. (css--mdn-after-render, css--mdn-find-symbol, css-lookup-symbol): New defuns. * test/lisp/textmodes/css-mode-tests.el (css-mdn-symbol-guessing): New test.
This commit is contained in:
parent
77888c8850
commit
68e8f4bb4a
3 changed files with 136 additions and 0 deletions
7
etc/NEWS
7
etc/NEWS
|
@ -613,6 +613,13 @@ HTML tags, classes and IDs using the 'completion-at-point' command.
|
|||
Completion candidates for HTML classes and IDs are retrieved from open
|
||||
HTML mode buffers.
|
||||
|
||||
---
|
||||
*** CSS mode now binds 'C-h s' to a function that will show
|
||||
information about a CSS construct (an at-rule, property, pseudo-class,
|
||||
pseudo-element, with the default being guessed from context). By
|
||||
default the information is looked up on the Mozilla Developer Network,
|
||||
but this can be customized using 'css-lookup-url-format'.
|
||||
|
||||
+++
|
||||
** Emacs now supports character name escape sequences in character and
|
||||
string literals. The syntax variants \N{character name} and
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
(require 'seq)
|
||||
(require 'sgml-mode)
|
||||
(require 'smie)
|
||||
(require 'eww)
|
||||
|
||||
(defgroup css nil
|
||||
"Cascading Style Sheets (CSS) editing mode."
|
||||
|
@ -621,6 +622,12 @@ cannot be completed sensibly: `custom-ident',
|
|||
(modify-syntax-entry ?- "_" st)
|
||||
st))
|
||||
|
||||
(defvar css-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map [remap info-lookup-symbol] 'css-lookup-symbol)
|
||||
map)
|
||||
"Keymap used in `css-mode'.")
|
||||
|
||||
(eval-and-compile
|
||||
(defconst css--uri-re
|
||||
(concat
|
||||
|
@ -1087,5 +1094,112 @@ pseudo-elements, pseudo-classes, at-rules, and bang-rules."
|
|||
(setq-local font-lock-defaults
|
||||
(list (scss-font-lock-keywords) nil t)))
|
||||
|
||||
|
||||
|
||||
(defvar css--mdn-lookup-history nil)
|
||||
|
||||
(defcustom css-lookup-url-format
|
||||
"https://developer.mozilla.org/en-US/docs/Web/CSS/%s?raw¯os"
|
||||
"Format for a URL where CSS documentation can be found.
|
||||
The format should include a single \"%s\" substitution.
|
||||
The name of the CSS property, @-id, pseudo-class, or pseudo-element
|
||||
to look up will be substituted there."
|
||||
:version "26.1"
|
||||
:type 'string
|
||||
:group 'css)
|
||||
|
||||
(defun css--mdn-after-render ()
|
||||
(setf header-line-format nil)
|
||||
(goto-char (point-min))
|
||||
(let ((window (get-buffer-window (current-buffer) 'visible)))
|
||||
(when window
|
||||
(when (re-search-forward "^Summary" nil 'move)
|
||||
(beginning-of-line)
|
||||
(set-window-start window (point))))))
|
||||
|
||||
(defconst css--mdn-symbol-regexp
|
||||
(concat "\\("
|
||||
;; @-ids.
|
||||
"\\(@" (regexp-opt css-at-ids) "\\)"
|
||||
"\\|"
|
||||
;; ;; Known properties.
|
||||
(regexp-opt css-property-ids t)
|
||||
"\\|"
|
||||
;; Pseudo-classes.
|
||||
"\\(:" (regexp-opt css-pseudo-class-ids) "\\)"
|
||||
"\\|"
|
||||
;; Pseudo-elements with either one or two ":"s.
|
||||
"\\(::?" (regexp-opt css-pseudo-element-ids) "\\)"
|
||||
"\\)")
|
||||
"Regular expression to match the CSS symbol at point.")
|
||||
|
||||
(defconst css--mdn-property-regexp
|
||||
(concat "\\_<" (regexp-opt css-property-ids t) "\\s-*\\(?:\\=\\|:\\)")
|
||||
"Regular expression to match a CSS property.")
|
||||
|
||||
(defconst css--mdn-completion-list
|
||||
(nconc
|
||||
;; @-ids.
|
||||
(mapcar (lambda (atrule) (concat "@" atrule)) css-at-ids)
|
||||
;; Pseudo-classes.
|
||||
(mapcar (lambda (class) (concat ":" class)) css-pseudo-class-ids)
|
||||
;; Pseudo-elements with either one or two ":"s.
|
||||
(mapcar (lambda (elt) (concat ":" elt)) css-pseudo-element-ids)
|
||||
(mapcar (lambda (elt) (concat "::" elt)) css-pseudo-element-ids)
|
||||
;; Properties.
|
||||
css-property-ids)
|
||||
"List of all symbols available for lookup via MDN.")
|
||||
|
||||
(defun css--mdn-find-symbol ()
|
||||
"A helper for `css-lookup-symbol' that finds the symbol at point.
|
||||
Returns the symbol, a string, or nil if none found."
|
||||
(save-excursion
|
||||
;; Skip backward over a word first.
|
||||
(skip-chars-backward "-[:alnum:] \t")
|
||||
;; Now skip ":" or "@" to see if it's a pseudo-element or at-id.
|
||||
(skip-chars-backward "@:")
|
||||
(if (looking-at css--mdn-symbol-regexp)
|
||||
(match-string-no-properties 0)
|
||||
(let ((bound (save-excursion
|
||||
(beginning-of-line)
|
||||
(point))))
|
||||
(when (re-search-backward css--mdn-property-regexp bound t)
|
||||
(match-string-no-properties 1))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun css-lookup-symbol (symbol)
|
||||
"Display the CSS documentation for SYMBOL, as found on MDN.
|
||||
When this command is used interactively, it picks a default
|
||||
symbol based on the CSS text before point -- either an @-keyword,
|
||||
a property name, a pseudo-class, or a pseudo-element, depending
|
||||
on what is seen near point."
|
||||
(interactive
|
||||
(list
|
||||
(let* ((sym (css--mdn-find-symbol))
|
||||
(enable-recursive-minibuffers t)
|
||||
(value (completing-read
|
||||
(if sym
|
||||
(format "Describe CSS symbol (default %s): " sym)
|
||||
"Describe CSS symbol: ")
|
||||
css--mdn-completion-list nil nil nil
|
||||
'css--mdn-lookup-history sym)))
|
||||
(if (equal value "") sym value))))
|
||||
(when symbol
|
||||
;; If we see a single-colon pseudo-element like ":after", turn it
|
||||
;; into "::after".
|
||||
(when (and (eq (aref symbol 0) ?:)
|
||||
(member (substring symbol 1) css-pseudo-element-ids))
|
||||
(setq symbol (concat ":" symbol)))
|
||||
(let ((url (format css-lookup-url-format symbol))
|
||||
(buffer (get-buffer-create "*MDN CSS*")))
|
||||
(save-selected-window
|
||||
;; Make sure to display the buffer before calling `eww', as
|
||||
;; that calls `pop-to-buffer-same-window'.
|
||||
(switch-to-buffer-other-window buffer)
|
||||
(with-current-buffer buffer
|
||||
(eww-mode)
|
||||
(add-hook 'eww-after-render-hook #'css--mdn-after-render nil t)
|
||||
(eww url))))))
|
||||
|
||||
(provide 'css-mode)
|
||||
;;; css-mode.el ends here
|
||||
|
|
|
@ -218,5 +218,20 @@
|
|||
(should (member "body" completions))
|
||||
(should-not (member "article" completions)))))
|
||||
|
||||
(ert-deftest css-mdn-symbol-guessing ()
|
||||
(dolist (item '(("@med" "ia" "@media")
|
||||
("@keyframes " "{" "@keyframes")
|
||||
("p::after" "" "::after")
|
||||
("p:before" "" ":before")
|
||||
("a:v" "isited" ":visited")
|
||||
("border-" "color: red" "border-color")
|
||||
("border-color: red" ";" "border-color")
|
||||
("border-color: red; color: green" ";" "color")))
|
||||
(with-temp-buffer
|
||||
(css-mode)
|
||||
(insert (nth 0 item))
|
||||
(save-excursion (insert (nth 1 item)))
|
||||
(should (equal (nth 2 item) (css--mdn-find-symbol))))))
|
||||
|
||||
(provide 'css-mode-tests)
|
||||
;;; css-mode-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue