;; -*- lexical-binding: t; -*- ;;; related to font (defun ekp-font-family (string &optional position) (format "%s" (font-get (font-at (or position 0) nil string) :family))) (defun ekp-font-monospace-p (font-family) (let* ((font (find-font (font-spec :family font-family))) (font-name (font-xlfd-name font)) (type (nth 10 (split-string font-name "-" t)))) ;; 'c' used in terminal (or (or (string= "m" type) (string= "c" type)) (let ((info (font-info font-name))) (and info (> (length info) 4) ;; 等宽字体的核心标志: 最大宽度等于平均宽度 (= (aref info 7) (aref info 11))))))) (defun ekp-get-latin-letter (string) (with-temp-buffer (insert string) (goto-char (point-min)) (while (and (< (point) (point-max)) (= 2 (char-width (char-after)))) (forward-char 1)) (unless (eobp) (buffer-substring (point) (1+ (point)))))) (defun ekp-get-cjk-letter (string) (with-temp-buffer (insert string) (goto-char (point-min)) (while (and (< (point) (point-max)) (= 1 (char-width (char-after)))) (forward-char 1)) (unless (eobp) (buffer-substring (point) (1+ (point)))))) (defun ekp-monospace-p (string) "判断字符串中的拉丁字母的字体是否等宽,返回字体名称" (if-let* ((letter (ekp-get-latin-letter string)) (font-family (ekp-font-family letter))) ;; return monospace font family (when (ekp-font-monospace-p font-family) font-family) ;; no latin letter in string, use default (face-attribute 'default :family))) (defun ekp-word-spacing-pixel (string) ;; font is monospace, use the pixel of blank ;; as word spacing pixel (if-let ((font-family (ekp-monospace-p string))) (string-pixel-width (propertize " " 'face `(:family ,font-family))) ;; font is not monospace, use the pixel of letter n ;; as word spacing pixel (let* ((letter (ekp-get-latin-letter string)) (font-family (ekp-font-family letter))) (string-pixel-width (propertize "n" 'face `(:family ,font-family)))))) (defun ekp-latin-font (string) (if-let ((letter (ekp-get-latin-letter string))) (ekp-font-family letter) (face-attribute 'default :family))) (defun ekp-cjk-font (string) (if-let ((letter (ekp-get-cjk-letter string))) (ekp-font-family letter) (ekp-font-family "牛"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun ekp-start-process-with-callback (process-name command-args callback &optional output-buffer) "执行命令(带参数)并在完成后调用回调" (let* ((buffer-name (generate-new-buffer-name (or output-buffer "*EKP Process Output*"))) (process (apply #'start-process process-name buffer-name command-args))) (set-process-sentinel process `(lambda (proc event) (if (string-match-p "finished" event) (when (memq (process-status proc) '(exit signal)) (unwind-protect (funcall ',callback proc (process-buffer proc)) (when (buffer-live-p (process-buffer proc)) (kill-buffer (process-buffer proc))))) (message "%s, please check %s" (string-trim event) ,buffer-name)))) process)) (defun ekp-rust-module-reload (module) (let ((tmpfile (make-temp-file (file-name-nondirectory module)))) (copy-file module tmpfile t) (module-load tmpfile))) (defun ekp-module-dir () (when-let ((root-dir (ekp-root-dir))) (expand-file-name "ekp_rust" root-dir))) (defun ekp-module-file () (when-let* ((module-dir (ekp-module-dir)) (filename (cond ((eq system-type 'darwin) "libekp.dylib") ((eq system-type 'windows-nt) "ekp.dll") (t "libekp.so")))) (expand-file-name (concat "target/release/" filename) module-dir))) (defun ekp-module-load () "Load rust module of ekp." (if (executable-find "cargo") (let ((file (ekp-module-file))) (if file (ekp-rust-module-reload file) (ekp-module-build))) (error "Please install cargo and add it to executable path!"))) (defun ekp-module-build () "Reload ekp rust module." (interactive) (if (executable-find "cargo") (ekp-start-process-with-callback "ekp-build" (cond ((eq system-type 'windows-nt) `("cmd.exe" "/c" ,(format "cd %s && cargo build -r" (ekp-module-dir)))) (t `("zsh" "-c" ,(format "cd %s && cargo build -r" (ekp-module-dir))))) (lambda (proc buffer) (ekp-rust-module-reload (ekp-module-file)) (message "ekp rust module reload success!"))) (error "Please install cargo and add it to executable path!"))) (defmacro ekp-setq (sym val) "Set the value of symbol SYM to VAL. If VAL is nil, set to the value of [SYM]-default." `(setq ,sym (or ,val ,(intern (concat (symbol-name sym) "-default"))))) (defun ekp-pixel-spacing (pixel) "Return a pixel spacing with a PIXEL pixel width." (if (= pixel 0) "" (propertize " " 'display `(space :width (,pixel))))) (defun ekp-cjk-punct-p (char) (or (and (>= char #x3000) (<= char #x303F)) (and (>= char #xFF00) (<= char #xFF60)))) (defun ekp-split-to-boxes (string) (with-temp-buffer (insert string) (goto-char (point-min)) (let ((state (char-width (seq-first string))) curr-str prev-str boxes) (while (not (eobp)) (let* ((char (char-after)) (str (char-to-string char))) (if (string-blank-p str) (when curr-str (push curr-str boxes) (setq curr-str nil)) (if (= state 1) (cond ((= 1 (string-width str)) (setq curr-str (concat curr-str str))) ((= 2 (string-width str)) (when curr-str (push curr-str boxes) (setq curr-str nil)) ;; switch to state 2 (push str boxes) (setq state 2))) (cond ((= 2 (string-width str)) (if (ekp-cjk-punct-p char) (progn (push (concat prev-str str) boxes) (setq prev-str nil)) (when prev-str (push prev-str boxes)) (setq prev-str str))) ((= 1 (string-width str)) ;; switch to state 1 (setq curr-str (concat curr-str str)) (setq state 1)))))) (forward-char 1)) (when curr-str (push curr-str boxes)) (vconcat (nreverse boxes))))) (defun ekp-clear-caches () (interactive) (setq ekp-caches (make-hash-table :test 'equal :size 100 :rehash-size 1.5 :weakness nil))) ;; (defvar ekp-par-num 8) ;; (defun ekp-strings (string n) ;; "Split STRING to average N parts but don't split in a word." ;; ;; used for rust ;; (let* ((size (length string)) ;; (each-size (/ size n)) ;; (str-ends (--map (if (= it n) size (* it each-size)) ;; (number-sequence 1 n))) ;; regions) ;; (with-temp-buffer ;; (insert string) ;; (goto-char (point-min)) ;; (let ((str-start 0) ;; (prev-end 0)) ;; (dolist (str-end str-ends) ;; (when (> str-end prev-end) ;; (goto-char (1+ str-end)) ;; (while (and (not (eobp)) ;; (not (eq ? (char-after))) ;; (< (char-width (char-after)) 2)) ;; (forward-char 1)) ;; (setq str-end (1- (point))) ;; (push (cons str-start str-end) regions) ;; (setq prev-end str-end) ;; (setq str-start str-end))))) ;; (vconcat (--map ;; (substring string (car it) (cdr it)) ;; (nreverse regions))))) (provide 'ekp-utils)