diff --git a/modules/app/regex/autoload.el b/modules/app/regex/autoload.el new file mode 100644 index 000000000..8b58505ea --- /dev/null +++ b/modules/app/regex/autoload.el @@ -0,0 +1,180 @@ +;;; app/regex/autoload.el + +(defvar +regex--text-buffer nil) +(defvar +regex--expr-buffer nil) +(defvar +regex--groups-buffer nil) + +;; +(defface +regex-match-0-face + '((t (:foreground "Black" :background "Red" :bold t))) + "" + :group 'doom) + +(defface +regex-match-1-face + '((t (:foreground "Black" :background "Blue" :bold t))) + "" + :group 'doom) + +(defface +regex-match-2-face + '((t (:foreground "Black" :background "Green" :bold t))) + "" + :group 'doom) + +(defvar +regex-faces + '(+regex-match-0-face +regex-match-1-face +regex-match-2-face) + "TODO") + +;; +(defvar +regex-mode-map + (let ((map (make-sparse-keymap))) + (evil-define-key* 'normal map + "\C-c\C-c" #'+regex-update-buffers + "\C-c\C-k" #'+regex/quit) + map) + "TODO") + +(define-minor-mode +regex-mode + "TODO" + :init-value nil + :global nil + :lighter "" + :keymap +regex-mode-map + (if +regex-mode + (add-hook 'after-change-functions #'+regex-update-buffers nil t) + (remove-hook 'after-change-functions #'+regex-update-buffers t))) + +;;;###autoload +(defun =regex (&optional dummy-text) + "Start the Regex IDE." + (interactive "P") + (condition-case ex + (progn + (setq +regex--expr-buffer (get-buffer-create "*doom-regex*") + +regex--text-buffer (if dummy-text (get-buffer-create "*doom-regex-text*") (current-buffer)) + +regex--groups-buffer (get-buffer-create "*doom-regex-groups*")) + (when dummy-text + (+workspace-switch +regex-workspace-name t) + (switch-to-buffer +regex--text-buffer) + (insert +regex-dummy-text)) + (doom-popup-buffer +regex--groups-buffer) + (doom-popup-buffer +regex--expr-buffer) + (with-current-buffer +regex--expr-buffer + (conf-mode) + (doom-buffer-mode +1) + (rainbow-delimiters-mode +1) + (linum-mode -1) + (+regex-mode +1) + (text-scale-set 3))) + ('error + (+regex/quit) + (error "Failed to open the Regexp IDE: %s" ex)))) + +;;;###autoload +(defun +regex/quit () + "TODO" + (interactive) + (kill-buffer +regex--expr-buffer) + (kill-buffer +regex--groups-buffer) + (when (and +regex--text-buffer (buffer-live-p +regex--text-buffer)) + (with-current-buffer +regex--text-buffer + (+regex-mode -1) + (remove-overlays))) + (setq +regex--expr-buffer nil + +regex--text-buffer nil + +regex--groups-buffer nil) + (when (equal (+workspace-current-name) +regex-workspace-name) + (kill-buffer +regex--text-buffer) + (+workspace/delete +regex-workspace-name))) + +;;;###autoload +(defun +regex-update-buffers (&optional beg end len) + (interactive) + (let ((regex (with-current-buffer +regex--expr-buffer (buffer-string)))) + (when (> (length regex) 0) + (with-current-buffer +regex--groups-buffer + (erase-buffer)) + (with-current-buffer +regex--text-buffer + (remove-overlays) + (save-excursion + (ignore-errors + (goto-char (point-min)) + (pcase +regex-default-backend + ('emacs (+regex-backend-emacs regex)) + ('perl (+regex-backend-perl regex)))))) + (with-current-buffer +regex--groups-buffer + (goto-char (point-min)))))) + + +;; --- backends --------------------------- + +(defun +regex--render-perl (regex sample) + "From " + (with-temp-buffer + (unless (string-match-p "^/.+/[gm]*$" regex) + (setq regex (concat "/" regex "/g"))) + (insert (format "@lines = ; +$line = join(\"\", @lines); +print \"(\"; +while ($line =~ m%smg) { + print \"(\", length($`), \" \", length($&), \" \"; + for $i (1 .. 20) { + if ($$i) { + my $group = $$i; + $group =~ s/([\\\\\"])/\\\\\\1/g; + print \"(\", $i, \" . \\\"\", $group, \"\\\") \"; + } + } + print \")\"; +} +print \")\"; +__DATA__ +%s" regex sample)) + (call-process-region (point-min) (point-max) "perl" t t) + (goto-char (point-min)) + (read (current-buffer)))) + +;;;###autoload +(defun +regex-backend-perl (regex) + "TODO" + (let ((results (+regex--render-perl regex (buffer-string))) + (i 0)) + (dolist (result results) + (let ((offset (nth 0 result)) + (length (nth 1 result)) + (matches (nthcdr 2 result))) + (overlay-put (make-overlay (1+ offset) (+ offset length 1)) + 'face (nth (mod i 3) +regex-faces)) + (cl-incf i) + (let ((match-zero (buffer-substring (1+ offset) (+ offset length 1)))) + (with-current-buffer +regex--groups-buffer + (insert (format "Group 0: '%s'\n" match-zero)))) + (dolist (match matches) + (with-current-buffer +regex--groups-buffer + (goto-char (point-max)) + (insert (format "Group %d: '%s'\n" (car match) + (cdr match))))) + (with-current-buffer +regex--groups-buffer + (insert ?\n)))))) + +;;;###autoload +(defun +regex-backend-emacs (regex) + "TODO" + (let ((i 0) + pos) + (while (and (setq pos (point)) + (re-search-forward regex nil t)) + (if (= (point) pos) + (forward-char 1) + (overlay-put (make-overlay (match-beginning 0) + (match-end 0)) + 'face (nth (mod i 3) +regex-faces)) + (cl-incf i) + (dotimes (i 10) + (when-let (text (match-string i)) + (save-match-data + (with-current-buffer +regex--groups-buffer + (goto-char (point-max)) + (insert (format "Group %d: '%s'\n" i text)))))) + (with-current-buffer +regex--groups-buffer + (insert ?\n)))))) + diff --git a/modules/app/regex/config.el b/modules/app/regex/config.el new file mode 100644 index 000000000..9b124f729 --- /dev/null +++ b/modules/app/regex/config.el @@ -0,0 +1,38 @@ +;;; app/regex/config.el + +;; Provides a Regex IDE, invoked by `=regex'. If opened with C-u, opens in a +;; separate frame with a dummy text buffer. +;; +;; WARNING: THIS IS A WORK IN PROGRESS + +(defvar +regex-workspace-name "*regex*" + "TODO") + +(defvar +regex-default-backend 'emacs + "The backend used to process regular expressions. +The `emacs' backend handles regular expressions directly. +The `perl' backend talks to a perl subprocess to do the handling.") + +(defvar +regex-dummy-text + "Welcome to DOOM Emacs, proudly hosted by the demons of hell! + +Edit the Expression & Text to see matches. Roll over matches or the expression +for details. Undo mistakes with ctrl-z. Save Favorites & Share expressions with +friends or the Community. Explore your results with Tools. A full Reference & +Help is available in the Library, or watch the video Tutorial. + +Sample text for testing: +abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ +0123456789 _+-.,!@#$%^&*();\/|<>\"' +12345 -98.7 3.141 .6180 9,000 +42 +555.123.4567 +1-(800)-555-2468 +foo@demo.net bar.ba@test.co.uk +www.demo.com http://foo.co.uk/ +http://regexr.com/foo.html?q=bar +https://mediatemple.net" + "TODO") + +(set! :popup + '("*doom-regex*" :size 4 :select t :noesc t :autokill t) + '("*doom-regex-groups*" :align left :size 30 :noselect t :noesc t :autokill t)) +