;;; make-cf-map.el --- -*- lexical-binding: t -*- ;; Copyright (C) 2024 Benson Chu ;; Author: Benson Chu ;; Created: [2024-04-26 08:11] ;; This file is not part of GNU Emacs ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;;; Code: (defvar cfmap-width nil) (make-variable-buffer-local 'cfmap-width) (defun cfmap--point-inside (p r) (and (< (car r) p) (< p (cdr r)))) (defun cfmap--inside (r1 r2) (and (cfmap--point-inside (car r1) r2) (cfmap--point-inside (cdr r1) r2))) (defun cfmap-overlapping (r1 r2) (or (cfmap--point-inside (car r2) r1) (cfmap--point-inside (cdr r2) r1) (cfmap--inside r1 r2))) (defun cfmap-transform (cfmap) (let ((new-list)) (dolist (l cfmap) (let ((a (car l)) (b (cdr l))) (push (if (< a b) (cons 'down (cons a b)) (cons 'up (cons b a))) new-list))) (sort new-list (lambda (x y) (let ((size1 (/ (abs (- (cadr x) (cddr x))) 5)) (size2 (/ (abs (- (cadr y) (cddr y))) 5))) (if (not (= size1 size2)) (< size1 size2) (< (cadr x) (cadr y)))))))) ;; (defvar cfmap-transformed (cfmap-transform cfmap-test)) (defface cfmap-face1 `((t ,(list :inherit 'default :foreground "#9E1CB2"))) nil) (defface cfmap-face2 `((t ,(list :inherit 'default :foreground "#47B04B"))) nil) (defface cfmap-face3 `((t ,(list :inherit 'default :foreground "#1194f6"))) nil) (defface cfmap-face4 `((t ,(list :inherit 'default :foreground "#C90067"))) nil) (defface cfmap-face5 `((t ,(list :inherit 'default :foreground "#FFED18"))) nil) (defface cfmap-face6 `((t ,(list :inherit 'default :foreground "white"))) nil) (defvar cfmap-faces '(cfmap-face1 cfmap-face2 cfmap-face3 cfmap-face4 cfmap-face5 cfmap-face6)) (defun cfmap-draw-arrow (dir start end arrow-length) (let ((arrow-face (nth (mod (/ (1- arrow-length) 2) (length cfmap-faces)) cfmap-faces))) (cl-labels ((my-insert (str) (insert (propertize str 'face arrow-face))) (insert-arrow-part (type num &optional end) (beginning-of-line) (if (eq type 'line) (progn (unless (looking-at-p "\n") (delete-forward-char 1)) (my-insert (if (= num 0) (if (eq dir 'up) "▲" "▼") "│"))) (let ((intersect (if (or (and end (looking-at-p "┌")) (and (not end) (looking-at-p "└"))) "├" (if end "└" "┌")))) (cond ((eq type 'ingress) (if (eq (point) (line-end-position)) (my-insert (concat intersect (make-string (1- arrow-length) ?─) "►")) (delete-forward-char 1) (my-insert intersect) (dotimes (i (1- arrow-length)) (let ((char (cond ((looking-at-p (rx (or "│" "┼" "▲" "▼"))) "┼") ((looking-at-p (rx (or "┌" "┬"))) "┬") ((looking-at-p (rx (or "└" "┴"))) "┴") (t "─")))) (unless (looking-at-p "\n") (delete-forward-char 1)) (my-insert char))) (unless (looking-at-p "\n") (delete-forward-char 1)) (my-insert "►"))) ((eq type 'egress) (if (eq (point) (line-end-position)) (my-insert (concat intersect (make-string arrow-length ?─))) (delete-forward-char 1) (my-insert intersect) (dotimes (i arrow-length) (let ((char (cond ((looking-at-p (rx (or "│" "┼" "▲" "▼"))) "┼") ((looking-at-p (rx (or "┌" "┬"))) "┬") ((looking-at-p (rx (or "└" "┴"))) "┴") (t "─")))) (unless (looking-at-p "\n") (delete-forward-char 1)) (my-insert char))))))))) (my-next-line () (forward-line 1))) (goto-line start) (insert-arrow-part (if (eq dir 'up) 'ingress 'egress) 1) (let ((i 0) (end (1- (abs (- start end))))) (while (< i end) (my-next-line) (insert-arrow-part 'line (mod i 5)) (cl-incf i)) (my-next-line)) (insert-arrow-part (if (eq dir 'up) 'egress 'ingress) 1 t)))) (defun cfmap-render-buffer (buffer cfmap) (with-current-buffer buffer (erase-buffer) (dotimes (i 4000) (insert "\n")) (let* ((remaining (cfmap-transform cfmap)) (arrow-length 3) previous-end new-list super-start super-end) (while (not (zerop (length remaining))) (overwrite-mode 1) (setq previous-end nil first-start nil new-list nil) (dolist (l remaining) (if (and previous-end (< (cadr l) previous-end)) (push l new-list) (cfmap-draw-arrow (car l) (cadr l) (cddr l) arrow-length)) (when (or (not super-start ) (< (cadr l) super-start)) (setq super-start (cadr l))) (when (or (not super-end ) (< super-end (cddr l))) (setq super-end (cddr l))) (let ((end (cddr l))) (when (or (not previous-end) (< previous-end end)) (setq previous-end end)))) (overwrite-mode -1) (setq remaining (reverse new-list) arrow-length (+ 2 arrow-length)) (when (not (zerop (length remaining))) (let ((start (save-excursion (goto-line super-start) (line-beginning-position))) (end (save-excursion (goto-line super-end) (line-beginning-position)))) (string-rectangle start end " ")))) (let ((start (save-excursion (goto-line super-start) (line-beginning-position))) (end (save-excursion (goto-line super-end) (line-beginning-position)))) (string-rectangle start end " ")) (setq cfmap-width arrow-length)))) (provide 'make-cf-map) ;;; make-cf-map.el ends here