Chrome 把历史记录保存在一个 SQLite 数据库中,用户也可以访问(Chrome 开着时,需要先拷贝一份):
~ $ cp '/Users/xcy/Library/Application Support/Google/Chrome/Profile 1/History' .
~ $ sqlite3 History 'select url, title from urls limit 2'
http://emacs-china.org/|Emacs China
https://emacs-china.org/|关于“Chrome 历史”的搜索结果 - Emacs China
~ $
我写了个 Helm 界面,用来搜索 Chrome 历史记录:
(defvar helm-chrome-history-db
"~/Library/Application Support/Google/Chrome/Profile 1/History"
"Chrome History SQLite Database.")
(defun helm-chrome-history-init ()
;; 缓存到 " *helm candidates:Chrome History*",和缓存到变量相比,省去了插入的
;; 时间。我的有 600 万字符,应该会一直占用 6 M 内存
(unless (helm-candidate-buffer)
(helm-init-candidates-in-buffer 'global
(with-temp-buffer
(let ((db "/tmp/History"))
(copy-file helm-chrome-history-db db t)
(unless (zerop (call-process "sqlite3" nil t nil db "select url, title from urls"))
(error "sqlite3 failed: %s" (buffer-string)))
(buffer-string))))))
(defvar helm-chrome-history-source
(helm-build-in-buffer-source "Chrome History"
:init #'helm-chrome-history-init
:action (helm-make-actions
"Browse URL"
(lambda (x) (browse-url (car (split-string x "|"))))
"EWW URL"
(lambda (x) (eww (car (split-string x "|")))))))
(defun helm-chrome-history ()
(interactive)
(helm :sources helm-chrome-history-source
:buffer "*Helm Chrome History*"
:full-frame t))
8 个赞
cireu
2
(require 'cl-lib)
(require 'subr-x)
(require 'pcase)
(defvar cm/counsel-ff-db-path
(car (file-expand-wildcards "~/.mozilla/firefox/*/places.sqlite")))
(defvar cm/counsel-ff-bookmarks-cache nil)
(defun cm/counsel-ensure-ff-db ()
(let* ((name (make-temp-name "ffdb"))
(path (expand-file-name name temporary-file-directory)))
(condition-case e
(copy-file cm/counsel-ff-db-path path)
(error "Failed to ensure firefox database: %s" e))
path))
(defun cm/counsel-prepare-bookmarks-candidates (&optional force-update?)
(counsel-require-program "sqlite3")
(or
(if force-update? nil cm/counsel-ff-bookmarks-cache)
(let* ((db-path (cm/counsel-ensure-ff-db)))
(with-temp-buffer
(let ((errno (call-process
"sqlite3" nil (current-buffer) nil "--ascii" db-path
(string-join '("SELECT bm.title, p.url"
"FROM moz_bookmarks AS bm INNER JOIN moz_places AS p"
"WHERE bm.fk = p.id;") " ")))
splitted)
(if (= errno 0)
(setq splitted
(mapcar
(lambda (it) (split-string it "\037")) ; ^_
(split-string
(string-trim
(buffer-substring-no-properties (point-min) (point-max)))
"\036" ; ^^
)))
(error "SQLite exited with error code %d" errno))
(setq cm/counsel-ff-bookmarks-cache
(cl-remove "" (mapcar (pcase-lambda (`(,title ,url))
(when (null url) (setq url title))
(list (format "%s %s"
title
(propertize url 'face 'link))
url))
splitted)
:test #'equal :key #'cadr)))))))
(defun cm/counsel-firefox-bookmarks (&optional force-update?)
(interactive "P")
(ivy-read "Firefox Bookmarks: " (cm/counsel-prepare-bookmarks-candidates
force-update?)
:history 'cm/counsel-firefox-bookmarks-history
:action (lambda (it) (browse-url (cadr it)))
:caller 'cm/counsel-firefox-bookmarks
:require-match t))
用ivy查询火狐的历史记录。
火狐数据库里的书签,url和title居然不是放在一起的。title在moz_bookmarks
表里,这个表的fk
列标记了对应URL在moz_places
表里的id
列。
3 个赞
cireu
3
花时间重构了一下,现在历史记录和书签都可以查询了。
依赖emacsql,主要是用了他的sql语句生成器
3 个赞
自 El Captain,Safari 把历史存在 ~/Library/Safari 下的 SQLite 数据库,不过在 Mojave 现在这个路径被做了特殊处理不能访问和复制。比较有趣的是通过 Finder 反而可以移动出历史文件,终端下所有对这个路径的操作都被禁止了。
看见listary 了,暴露了你是windows用户的事实
cireu
7
我觉得listary比everything用户体验好多了,可惜不能和emacs很好的配合,另外现在不收钱各种阉割功能基本没法用
Chrome 有个很像 helm 的插件,也是做类似的事
2 个赞
可以在 README 上加个 MELPA 徽章,别人看到就知道了。
1 个赞
使用主题上的脚本,放在config.el ,启动emacs,出现以下问题:
–debug-init 日志:
Debugger entered--Lisp error: (void-function helm-build-in-buffer-source)
(helm-build-in-buffer-source "Chrome History" :init #'helm-chrome-history-init :action (helm-make-actions "Browse URL" #'(lambda (x) (browse-url (car (split-string x "|")))) "EWW URL" #'(lambda (x) (eww (car (split-string x "|"))))))
(defvar helm-chrome-history-source (helm-build-in-buffer-source "Chrome History" :init #'helm-chrome-history-init :action (helm-make-actions "Browse URL" #'(lambda (x) (browse-url (car (split-string x "|")))) "EWW URL" #'(lambda (x) (eww (car (split-string x "|")))))))
eval-buffer(#<buffer *load*-591807> nil "/Users/boyer/hsg/hugo/iDoom.d/modules/boyer/eww/co..." nil t) ; Reading at buffer position 4653
load-with-code-conversion("/Users/boyer/hsg/hugo/iDoom.d/modules/boyer/eww/co..." "/Users/boyer/hsg/hugo/iDoom.d/modules/boyer/eww/co..." t t)
load("/Users/boyer/hsg/hugo/iDoom.d/modules/boyer/eww/co..." t nomessage)
(let (file-name-handler-alist) (load (expand-file-name file (plist-get plist :path)) t 'nomessage))
(condition-case e (let (file-name-handler-alist) (load (expand-file-name file (plist-get plist :path)) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name file (plist-get plist :path)) (plist-get plist :path))))
(let ((doom--current-module module) (doom--current-flags (plist-get plist :flags)) (inhibit-redisplay t)) (condition-case e (let (file-name-handler-alist) (load (expand-file-name file (plist-get plist :path)) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name file (plist-get plist :path)) (plist-get plist :path)))))
(closure ((file . "config") t) (module plist) (let ((doom--current-module module) (doom--current-flags (plist-get plist :flags)) (inhibit-redisplay t)) (condition-case e (let (file-name-handler-alist) (load (expand-file-name file (plist-get plist :path)) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name file (plist-get plist :path)) (plist-get plist :path))))))((:boyer . eww) (:flags nil :path "/Users/boyer/hsg/hugo/iDoom.d/modules/boyer/eww/"))
maphash((closure ((file . "config") t) (module plist) (let ((doom--current-module module) (doom--current-flags (plist-get plist :flags)) (inhibit-redisplay t)) (condition-case e (let (file-name-handler-alist) (load (expand-file-name file (plist-get plist :path)) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name file (plist-get plist :path)) (plist-get plist :path)))))) #<hash-table equal 85/97 0x1ff7e50571e5>)
(if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let (file-name-handler-alist) (load (expand-file-name "config" doom-private-dir) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name "config" doom-private-dir) doom-private-dir))) (if custom-file (progn (load custom-file 'noerror (not doom-debug-mode)))))
(progn (if doom-debug-p (progn (let ((inhibit-message (active-minibuffer-window))) (message #("DOOM Initializing user config" 0 5 (face font-lock-comment-face)))))) (maphash (doom-module-loader doom-module-init-file) doom-modules) (doom-run-hooks 'doom-before-init-modules-hook) (if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let (file-name-handler-alist) (load (expand-file-name "config" doom-private-dir) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name "config" doom-private-dir) doom-private-dir))) (if custom-file (progn (load custom-file 'noerror (not doom-debug-mode))))))
(if init-p (progn (if doom-debug-p (progn (let ((inhibit-message (active-minibuffer-window))) (message #("DOOM Initializing user config" 0 5 (face font-lock-comment-face)))))) (maphash (doom-module-loader doom-module-init-file) doom-modules) (doom-run-hooks 'doom-before-init-modules-hook) (if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let (file-name-handler-alist) (load (expand-file-name "config" doom-private-dir) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name "config" doom-private-dir) doom-private-dir))) (if custom-file (progn (load custom-file 'noerror (not doom-debug-mode)))))) nil)
(let* ((init-p (and t (condition-case e (let (file-name-handler-alist) (load (expand-file-name doom-module-init-file doom-private-dir) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name doom-module-init-file doom-private-dir) doom-private-dir)))))) (if init-p (progn (if doom-debug-p (progn (let ((inhibit-message ...)) (message #("DOOM Initializing user config" 0 5 ...))))) (maphash (doom-module-loader doom-module-init-file) doom-modules) (doom-run-hooks 'doom-before-init-modules-hook) (if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let (file-name-handler-alist) (load (expand-file-name "config" doom-private-dir) t 'nomessage)) ((debug doom-error) (signal (car e) (cdr e))) ((debug error) (doom--handle-load-error e (expand-file-name "config" doom-private-dir) doom-private-dir))) (if custom-file (progn (load custom-file 'noerror (not doom-debug-mode)))))) nil))
(progn (setq doom-init-modules-p t) (if no-config-p nil (if doom-debug-p (progn (let ((inhibit-message (active-minibuffer-window))) (message #("DOOM Initializing core modules" 0 5 (face font-lock-comment-face)))))) (doom-initialize-core-modules)) (let* ((init-p (and t (condition-case e (let (file-name-handler-alist) (load ... t ...)) ((debug doom-error) (signal ... ...)) ((debug error) (doom--handle-load-error e ... doom-private-dir)))))) (if init-p (progn (if doom-debug-p (progn (let (...) (message ...)))) (maphash (doom-module-loader doom-module-init-file) doom-modules) (doom-run-hooks 'doom-before-init-modules-hook) (if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let (file-name-handler-alist) (load ... t ...)) ((debug doom-error) (signal ... ...)) ((debug error) (doom--handle-load-error e ... doom-private-dir))) (if custom-file (progn (load custom-file ... ...))))) nil)))
(if (or force-p (not doom-init-modules-p)) (progn (setq doom-init-modules-p t) (if no-config-p nil (if doom-debug-p (progn (let ((inhibit-message ...)) (message #("DOOM Initializing core modules" 0 5 ...))))) (doom-initialize-core-modules)) (let* ((init-p (and t (condition-case e (let ... ...) (... ...) (... ...))))) (if init-p (progn (if doom-debug-p (progn (let ... ...))) (maphash (doom-module-loader doom-module-init-file) doom-modules) (doom-run-hooks 'doom-before-init-modules-hook) (if no-config-p nil (maphash (doom-module-loader doom-module-config-file) doom-modules) (doom-run-hooks 'doom-init-modules-hook) (condition-case e (let ... ...) (... ...) (... ...)) (if custom-file (progn ...)))) nil))))
doom-initialize-modules()
eval-buffer(#<buffer *load*> nil "/Users/boyer/.emacs.d/init.el" nil t) ; Reading at buffer position 2803
load-with-code-conversion("/Users/boyer/.emacs.d/init.el" "/Users/boyer/.emacs.d/init.el" t t)
load("/Users/boyer/.emacs.d/init" noerror nomessage)
#f(compiled-function (filename-function &optional alternate-filename-function load-defaults) "Load a user init-file.\nFILENAME-FUNCTION is called with no arguments and should return\nthe name of the init-file to load. If this file cannot be\nloaded, and ALTERNATE-FILENAME-FUNCTION is non-nil, then it is\ncalled with no arguments and should return the name of an\nalternate init-file to load. If LOAD-DEFAULTS is non-nil, then\nload default.el after the init-file, unless `inhibit-default-init'\nis non-nil.\n\nThis function sets `user-init-file' to the name of the loaded\ninit-file, or to a default value if loading is not possible." #<bytecode 0x175e3ee983650d4c>)(#f(compiled-function () #<bytecode -0x1493560aa6ec0a7>) #f(compiled-function () #<bytecode -0x1f3c6eaddc0e6db5>) t)
apply(#f(compiled-function (filename-function &optional alternate-filename-function load-defaults) "Load a user init-file.\nFILENAME-FUNCTION is called with no arguments and should return\nthe name of the init-file to load. If this file cannot be\nloaded, and ALTERNATE-FILENAME-FUNCTION is non-nil, then it is\ncalled with no arguments and should return the name of an\nalternate init-file to load. If LOAD-DEFAULTS is non-nil, then\nload default.el after the init-file, unless `inhibit-default-init'\nis non-nil.\n\nThis function sets `user-init-file' to the name of the loaded\ninit-file, or to a default value if loading is not possible." #<bytecode 0x175e3ee983650d4c>) (#f(compiled-function () #<bytecode -0x1493560aa6ec0a7>) #f(compiled-function () #<bytecode -0x1f3c6eaddc0e6db5>) t))
startup--load-user-init-file(#f(compiled-function () #<bytecode -0x1493560aa6ec0a7>) #f(compiled-function () #<bytecode -0x1f3c6eaddc0e6db5>) t)
command-line()
normal-top-level()
谢谢,发现doom配置下的init.el 没有开启helm。
要是能用Emacs
管理浏览器tab
就好了, 可以用org-mode
分组, 写笔记, 关闭, 同步, 搜索, 备份 etc
aeghn
20
这个可以的,用 native host messaging 插件,然后用 dbus 传消息。