From a7b79d54688cb518b377ab38d237b9822746f8f4 Mon Sep 17 00:00:00 2001 From: KemoNine Date: Thu, 25 Aug 2022 08:56:58 -0400 Subject: [PATCH] remove xeft config ; will be using notdeft next / going forward --- org/init.el | 9 - org/xeft/Makefile | 23 - org/xeft/README.md | 84 --- org/xeft/gitignore | 2 - org/xeft/module/emacs-module-prelude.h | 164 ------ org/xeft/module/emacs-module.h | 763 ------------------------- org/xeft/module/xapian-lite-internal.h | 40 -- org/xeft/module/xapian-lite.cc | 446 --------------- org/xeft/xapian-lite.dll | Bin 249430 -> 0 bytes org/xeft/xeft.el | 760 ------------------------ 10 files changed, 2291 deletions(-) delete mode 100644 org/xeft/Makefile delete mode 100644 org/xeft/README.md delete mode 100644 org/xeft/gitignore delete mode 100644 org/xeft/module/emacs-module-prelude.h delete mode 100644 org/xeft/module/emacs-module.h delete mode 100644 org/xeft/module/xapian-lite-internal.h delete mode 100644 org/xeft/module/xapian-lite.cc delete mode 100644 org/xeft/xapian-lite.dll delete mode 100644 org/xeft/xeft.el diff --git a/org/init.el b/org/init.el index 6b8a391..0e834b1 100644 --- a/org/init.el +++ b/org/init.el @@ -80,15 +80,6 @@ (setq org-support-shift-select t) (setq org-src-fontify-natively t) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; xeft search tool -; (add-to-list 'load-path "~/.emacs.d.profiles/org/xeft") -; (require 'xeft) -; (setq xeft-database "~/.emacs.d.profiles/org/deft") -; (setq xeft-directory "~/org/") -; (setq xeft-default-extension "org") -; (setq xeft-recursive t) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; tags (load "~/.emacs.d.profiles/org/config-org-tags") diff --git a/org/xeft/Makefile b/org/xeft/Makefile deleted file mode 100644 index a57a749..0000000 --- a/org/xeft/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -.POSIX: -# Even if this is unnecessary, it doesn’t hurt. -PREFIX=/usr/local -CXX=g++ -CXXFLAGS=-fPIC -I$(PREFIX)/include -LDFLAGS=-L$(PREFIX)/lib -LDLIBS=-lxapian - -# Dylib extensions. -ifeq ($(OS),Windows_NT) - SOEXT = dll -endif -ifeq ($(shell uname),Darwin) - SOEXT = dylib -else - SOEXT = so -endif - -xapian-lite.dll: module/xapian-lite.cc - $(CXX) $< -o $@ -static -municode -lWs2_32 $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) - -clean: - rm -f *.so *.o diff --git a/org/xeft/README.md b/org/xeft/README.md deleted file mode 100644 index 357a355..0000000 --- a/org/xeft/README.md +++ /dev/null @@ -1,84 +0,0 @@ -![Demo gif](./demo.gif) - -# Usage - -To use Xeft the note searching interface, install it and type `M-x -xeft RET` to bring up the panel. If the dynamic module doesn’t already -exists, you are prompted to download or compile it automatically. If -you choose to download the module, no more action is required. If you -want to compile the module locally, refer to the next section for -prerequisites for compiling the module. - -Once the xeft buffer is up, type the search phrase in the first line. -Press `C-n` and `C-p` to go through each file. You can preview a file -in another window by pressing `SPC` on a file, or click the file with -the mouse. Press `RET` to open the file in the current window. - -Directory `xeft-directory` stores note files, directory -`xeft-database` stores the database. Xeft uses -`xeft-default-extension` to create new files, and it ignores files -with `xeft-ignore-extension`. - -By default, Xeft only searches for first level files in -`xeft-directory`, to make it search recursively, set `xeft-recursive` -to t. - -See the “xeft” customize group for more custom options and faces. - -# Queries - -On search queries: - -Since Xeft uses Xapian, it supports the query syntax Xapian supports: - -``` -AND, NOT, OR, XOR and parenthesizes -+word1 -word2 which matches documents that contains WORD1 but not - WORD2. -word1 NEAR word2 which matches documents in where word1 is near word2. -word1 ADJ word2 which matches documents in where word1 is near word2 - and word1 comes before word2 -"word1 word2" which matches exactly “word1 word2” -``` - -Xeft deviates from Xapian in one aspect: consecutive phrases have -implied `AND` between them. So `word1 word2 word3` is actually seen as -`word1 AND word2 AND word3`. - -See https://xapian.org/docs/queryparser.html for Xapian’s official -documentation on query syntax. - -# building the dynamic module - -To build the module, you need to have Xapian installed. On Mac, it can -be installed with macports by - -```shell -sudo port install xapian-core -``` - -Then, build the module by - -```shell -make PREFIX=/opt/local -``` - -Here `/opt/local` is the default prefix of macports, which is what I -used to install Xapian. Homebrew and Linux users probably can leave it -empty. - -I can’t test it but on windows you can get msys2 and -`mingw-w64-x86_64-xapian-core` and `make` should just work. Thanks to -pRot0ta1p for reporting this. - -# notdeft - -I owe many thanks to the author of notdeft. I don’t really know C++ or -Xapian, without reading his code I wouldn’t be able to write Xeft. - -Also, if you want a more powerful searching experience, you will be -happier using notdeft instead. - -# Xapian dynamic module - -I wrote a xapian dynamic module that you can use too. Check it out at . diff --git a/org/xeft/gitignore b/org/xeft/gitignore deleted file mode 100644 index e2d2375..0000000 --- a/org/xeft/gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.so -*.o \ No newline at end of file diff --git a/org/xeft/module/emacs-module-prelude.h b/org/xeft/module/emacs-module-prelude.h deleted file mode 100644 index 4edb9d1..0000000 --- a/org/xeft/module/emacs-module-prelude.h +++ /dev/null @@ -1,164 +0,0 @@ -#include "emacs-module.h" -#include -#include -#include -#include - -#ifndef EMACS_MODULE_PRELUDE_H -#define EMACS_MODULE_PRELUDE_H - -#define EMP_MAJOR_VERSION 1 -#define EMP_MINOR_VERSION 0 -#define EMP_PATCH_VERSION 0 - - -/* - Copy a Lisp string VALUE into BUFFER, and store the string size in - SIZE. A user doesn’t need to allocate BUFFER, but it is the user’s - responsibility to free it. If failed, return false, and the buffer - doesn’t need to be freed. - */ -bool -emp_copy_string_contents -(emacs_env *env, emacs_value value, char **buffer, size_t *size) -/* Copied from Pillipp’s document. I commented out assertions. */ -{ - ptrdiff_t buffer_size; - if (!env->copy_string_contents (env, value, NULL, &buffer_size)) - return false; - /* assert (env->non_local_exit_check (env) == emacs_funcall_exit_return); */ - /* assert (buffer_size > 0); */ - *buffer = (char*) malloc ((size_t) buffer_size); - if (*buffer == NULL) - { - env->non_local_exit_signal (env, env->intern (env, "memory-full"), - env->intern (env, "nil")); - return false; - } - ptrdiff_t old_buffer_size = buffer_size; - if (!env->copy_string_contents (env, value, *buffer, &buffer_size)) - { - free (*buffer); - *buffer = NULL; - return false; - } - /* assert (env->non_local_exit_check (env) == emacs_funcall_exit_return); */ - /* assert (buffer_size == old_buffer_size); */ - *size = (size_t) (buffer_size - 1); - return true; -} - -/* - Return a Lisp string. This is basically env->make_string except that - it calls strlen for you. - */ -emacs_value -emp_build_string (emacs_env *env, const char *string) -{ - return env->make_string (env, string, strlen (string)); -} - -/* - Intern NAME to a symbol. NAME has to be all-ASCII. - */ -emacs_value -emp_intern (emacs_env *env, const char *name) -{ - return env->intern (env, name); -} - -/* - Call a function named FN which takes NARGS number of arguments. - Example: funcall (env, "cons", 2, car, cdr); - */ -emacs_value -emp_funcall (emacs_env *env, const char* fn, ptrdiff_t nargs, ...) -{ - va_list argv; - va_start (argv, nargs); - emacs_value *args = (emacs_value *) malloc(nargs * sizeof(emacs_value)); - for (int idx = 0; idx < nargs; idx++) - { - args[idx] = va_arg (argv, emacs_value); - } - va_end (argv); - emacs_value val = env->funcall (env, emp_intern (env, fn), nargs, args); - free (args); - return val; -} - -/* - Provide FEATURE like ‘provide’ in Lisp. -*/ -void -emp_provide (emacs_env *env, const char *feature) -{ - emp_funcall (env, "provide", 1, emp_intern (env, feature)); -} - -/* - Raise a signal where NAME is the signal name and MESSAGE is the - error message. - */ -void -emp_signal_message1 -(emacs_env *env, const char *name, const char *message) -{ - env->non_local_exit_signal - (env, env->intern (env, name), - emp_funcall (env, "cons", 2, - env->make_string (env, message, strlen (message)), - emp_intern (env, "nil"))); -} - -/* - Define an error like ‘define-error’. - */ -void -emp_define_error -(emacs_env *env, const char *name, - const char *description, const char *parent) -{ - emp_funcall (env, "define-error", 3, - emp_intern (env, name), - env->make_string (env, description, strlen (description)), - emp_intern (env, parent)); -} - -/* - Return true if VAL is symbol nil. - */ -bool -emp_nilp (emacs_env *env, emacs_value val) -{ - return !env->is_not_nil (env, val); -} - -/* - Define a function NAME. The number of arguments that the function - takes is between MIN_ARITY and MAX_ARITY. FUNCTION is a function - with signature - - static emacs_value - function - (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) - EMACS_NOEXCEPT - - DOCUMENTATION is the docstring for FUNCTION. - */ -void -emp_define_function -(emacs_env *env, const char *name, ptrdiff_t min_arity, - ptrdiff_t max_arity, - emacs_value (*function) (emacs_env *env, - ptrdiff_t nargs, - emacs_value* args, - void *data) EMACS_NOEXCEPT, - const char *documentation) -{ - emacs_value fn = env->make_function - (env, min_arity, max_arity, function, documentation, NULL); - emp_funcall (env, "fset", 2, emp_intern (env, name), fn); -} - -#endif /* EMACS_MODULE_PRELUDE_H */ diff --git a/org/xeft/module/emacs-module.h b/org/xeft/module/emacs-module.h deleted file mode 100644 index 1185c06..0000000 --- a/org/xeft/module/emacs-module.h +++ /dev/null @@ -1,763 +0,0 @@ -/* emacs-module.h - GNU Emacs module API. - -Copyright (C) 2015-2021 Free Software Foundation, Inc. - -This file is part of GNU Emacs. - -GNU Emacs 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. - -GNU Emacs 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 GNU Emacs. If not, see . */ - -/* -This file defines the Emacs module API. Please see the chapter -`Dynamic Modules' in the GNU Emacs Lisp Reference Manual for -information how to write modules and use this header file. -*/ - -#ifndef EMACS_MODULE_H -#define EMACS_MODULE_H - -#include -#include -#include - -#ifndef __cplusplus -#include -#endif - -#define EMACS_MAJOR_VERSION 28 - -#if defined __cplusplus && __cplusplus >= 201103L -# define EMACS_NOEXCEPT noexcept -#else -# define EMACS_NOEXCEPT -#endif - -#if defined __cplusplus && __cplusplus >= 201703L -# define EMACS_NOEXCEPT_TYPEDEF noexcept -#else -# define EMACS_NOEXCEPT_TYPEDEF -#endif - -#if 3 < __GNUC__ + (3 <= __GNUC_MINOR__) -# define EMACS_ATTRIBUTE_NONNULL(...) \ - __attribute__ ((__nonnull__ (__VA_ARGS__))) -#elif (defined __has_attribute \ - && (!defined __clang_minor__ \ - || 3 < __clang_major__ + (5 <= __clang_minor__))) -# if __has_attribute (__nonnull__) -# define EMACS_ATTRIBUTE_NONNULL(...) \ - __attribute__ ((__nonnull__ (__VA_ARGS__))) -# endif -#endif -#ifndef EMACS_ATTRIBUTE_NONNULL -# define EMACS_ATTRIBUTE_NONNULL(...) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Current environment. */ -typedef struct emacs_env_28 emacs_env; - -/* Opaque pointer representing an Emacs Lisp value. - BEWARE: Do not assume NULL is a valid value! */ -typedef struct emacs_value_tag *emacs_value; - -enum { emacs_variadic_function = -2 }; - -/* Struct passed to a module init function (emacs_module_init). */ -struct emacs_runtime -{ - /* Structure size (for version checking). */ - ptrdiff_t size; - - /* Private data; users should not touch this. */ - struct emacs_runtime_private *private_members; - - /* Return an environment pointer. */ - emacs_env *(*get_environment) (struct emacs_runtime *runtime) - EMACS_ATTRIBUTE_NONNULL (1); -}; - -/* Type aliases for function pointer types used in the module API. - Note that we don't use these aliases directly in the API to be able - to mark the function arguments as 'noexcept' before C++20. - However, users can use them if they want. */ - -/* Function prototype for the module Lisp functions. These must not - throw C++ exceptions. */ -typedef emacs_value (*emacs_function) (emacs_env *env, ptrdiff_t nargs, - emacs_value *args, - void *data) - EMACS_NOEXCEPT_TYPEDEF EMACS_ATTRIBUTE_NONNULL (1); - -/* Function prototype for module user-pointer and function finalizers. - These must not throw C++ exceptions. */ -typedef void (*emacs_finalizer) (void *data) EMACS_NOEXCEPT_TYPEDEF; - -/* Possible Emacs function call outcomes. */ -enum emacs_funcall_exit -{ - /* Function has returned normally. */ - emacs_funcall_exit_return = 0, - - /* Function has signaled an error using `signal'. */ - emacs_funcall_exit_signal = 1, - - /* Function has exit using `throw'. */ - emacs_funcall_exit_throw = 2 -}; - -/* Possible return values for emacs_env.process_input. */ -enum emacs_process_input_result -{ - /* Module code may continue */ - emacs_process_input_continue = 0, - - /* Module code should return control to Emacs as soon as possible. */ - emacs_process_input_quit = 1 -}; - -/* Define emacs_limb_t so that it is likely to match GMP's mp_limb_t. - This micro-optimization can help modules that use mpz_export and - mpz_import, which operate more efficiently on mp_limb_t. It's OK - (if perhaps a bit slower) if the two types do not match, and - modules shouldn't rely on the two types matching. */ -typedef size_t emacs_limb_t; -#define EMACS_LIMB_MAX SIZE_MAX - -struct emacs_env_25 -{ - /* Structure size (for version checking). */ - ptrdiff_t size; - - /* Private data; users should not touch this. */ - struct emacs_env_private *private_members; - - /* Memory management. */ - - emacs_value (*make_global_ref) (emacs_env *env, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*free_global_ref) (emacs_env *env, emacs_value global_value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Non-local exit handling. */ - - enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_clear) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - enum emacs_funcall_exit (*non_local_exit_get) - (emacs_env *env, emacs_value *symbol, emacs_value *data) - EMACS_ATTRIBUTE_NONNULL(1, 2, 3); - - void (*non_local_exit_signal) (emacs_env *env, - emacs_value symbol, emacs_value data) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_throw) (emacs_env *env, - emacs_value tag, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Function registration. */ - - emacs_value (*make_function) (emacs_env *env, - ptrdiff_t min_arity, - ptrdiff_t max_arity, - emacs_value (*func) (emacs_env *env, - ptrdiff_t nargs, - emacs_value* args, - void *data) - EMACS_NOEXCEPT - EMACS_ATTRIBUTE_NONNULL(1), - const char *docstring, - void *data) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - emacs_value (*funcall) (emacs_env *env, - emacs_value func, - ptrdiff_t nargs, - emacs_value* args) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*intern) (emacs_env *env, const char *name) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Type conversion. */ - - emacs_value (*type_of) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*is_not_nil) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*eq) (emacs_env *env, emacs_value a, emacs_value b) - EMACS_ATTRIBUTE_NONNULL(1); - - intmax_t (*extract_integer) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_integer) (emacs_env *env, intmax_t n) - EMACS_ATTRIBUTE_NONNULL(1); - - double (*extract_float) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_float) (emacs_env *env, double d) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Copy the content of the Lisp string VALUE to BUFFER as an utf8 - null-terminated string. - - SIZE must point to the total size of the buffer. If BUFFER is - NULL or if SIZE is not big enough, write the required buffer size - to SIZE and return true. - - Note that SIZE must include the last null byte (e.g. "abc" needs - a buffer of size 4). - - Return true if the string was successfully copied. */ - - bool (*copy_string_contents) (emacs_env *env, - emacs_value value, - char *buf, - ptrdiff_t *len) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - /* Create a Lisp string from a utf8 encoded string. */ - emacs_value (*make_string) (emacs_env *env, - const char *str, ptrdiff_t len) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Embedded pointer type. */ - emacs_value (*make_user_ptr) (emacs_env *env, - void (*fin) (void *) EMACS_NOEXCEPT, - void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void *(*get_user_ptr) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr)) - (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_finalizer) (emacs_env *env, emacs_value arg, - void (*fin) (void *) EMACS_NOEXCEPT) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Vector functions. */ - emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index, - emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector) - EMACS_ATTRIBUTE_NONNULL(1); -}; - -struct emacs_env_26 -{ - /* Structure size (for version checking). */ - ptrdiff_t size; - - /* Private data; users should not touch this. */ - struct emacs_env_private *private_members; - - /* Memory management. */ - - emacs_value (*make_global_ref) (emacs_env *env, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*free_global_ref) (emacs_env *env, emacs_value global_value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Non-local exit handling. */ - - enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_clear) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - enum emacs_funcall_exit (*non_local_exit_get) - (emacs_env *env, emacs_value *symbol, emacs_value *data) - EMACS_ATTRIBUTE_NONNULL(1, 2, 3); - - void (*non_local_exit_signal) (emacs_env *env, - emacs_value symbol, emacs_value data) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_throw) (emacs_env *env, - emacs_value tag, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Function registration. */ - - emacs_value (*make_function) (emacs_env *env, - ptrdiff_t min_arity, - ptrdiff_t max_arity, - emacs_value (*func) (emacs_env *env, - ptrdiff_t nargs, - emacs_value* args, - void *data) - EMACS_NOEXCEPT - EMACS_ATTRIBUTE_NONNULL(1), - const char *docstring, - void *data) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - emacs_value (*funcall) (emacs_env *env, - emacs_value func, - ptrdiff_t nargs, - emacs_value* args) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*intern) (emacs_env *env, const char *name) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Type conversion. */ - - emacs_value (*type_of) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*is_not_nil) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*eq) (emacs_env *env, emacs_value a, emacs_value b) - EMACS_ATTRIBUTE_NONNULL(1); - - intmax_t (*extract_integer) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_integer) (emacs_env *env, intmax_t n) - EMACS_ATTRIBUTE_NONNULL(1); - - double (*extract_float) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_float) (emacs_env *env, double d) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Copy the content of the Lisp string VALUE to BUFFER as an utf8 - null-terminated string. - - SIZE must point to the total size of the buffer. If BUFFER is - NULL or if SIZE is not big enough, write the required buffer size - to SIZE and return true. - - Note that SIZE must include the last null byte (e.g. "abc" needs - a buffer of size 4). - - Return true if the string was successfully copied. */ - - bool (*copy_string_contents) (emacs_env *env, - emacs_value value, - char *buf, - ptrdiff_t *len) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - /* Create a Lisp string from a utf8 encoded string. */ - emacs_value (*make_string) (emacs_env *env, - const char *str, ptrdiff_t len) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Embedded pointer type. */ - emacs_value (*make_user_ptr) (emacs_env *env, - void (*fin) (void *) EMACS_NOEXCEPT, - void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void *(*get_user_ptr) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr)) - (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_finalizer) (emacs_env *env, emacs_value arg, - void (*fin) (void *) EMACS_NOEXCEPT) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Vector functions. */ - emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index, - emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Returns whether a quit is pending. */ - bool (*should_quit) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); -}; - -struct emacs_env_27 -{ - /* Structure size (for version checking). */ - ptrdiff_t size; - - /* Private data; users should not touch this. */ - struct emacs_env_private *private_members; - - /* Memory management. */ - - emacs_value (*make_global_ref) (emacs_env *env, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*free_global_ref) (emacs_env *env, emacs_value global_value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Non-local exit handling. */ - - enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_clear) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - enum emacs_funcall_exit (*non_local_exit_get) - (emacs_env *env, emacs_value *symbol, emacs_value *data) - EMACS_ATTRIBUTE_NONNULL(1, 2, 3); - - void (*non_local_exit_signal) (emacs_env *env, - emacs_value symbol, emacs_value data) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_throw) (emacs_env *env, - emacs_value tag, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Function registration. */ - - emacs_value (*make_function) (emacs_env *env, - ptrdiff_t min_arity, - ptrdiff_t max_arity, - emacs_value (*func) (emacs_env *env, - ptrdiff_t nargs, - emacs_value* args, - void *data) - EMACS_NOEXCEPT - EMACS_ATTRIBUTE_NONNULL(1), - const char *docstring, - void *data) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - emacs_value (*funcall) (emacs_env *env, - emacs_value func, - ptrdiff_t nargs, - emacs_value* args) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*intern) (emacs_env *env, const char *name) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Type conversion. */ - - emacs_value (*type_of) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*is_not_nil) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*eq) (emacs_env *env, emacs_value a, emacs_value b) - EMACS_ATTRIBUTE_NONNULL(1); - - intmax_t (*extract_integer) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_integer) (emacs_env *env, intmax_t n) - EMACS_ATTRIBUTE_NONNULL(1); - - double (*extract_float) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_float) (emacs_env *env, double d) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Copy the content of the Lisp string VALUE to BUFFER as an utf8 - null-terminated string. - - SIZE must point to the total size of the buffer. If BUFFER is - NULL or if SIZE is not big enough, write the required buffer size - to SIZE and return true. - - Note that SIZE must include the last null byte (e.g. "abc" needs - a buffer of size 4). - - Return true if the string was successfully copied. */ - - bool (*copy_string_contents) (emacs_env *env, - emacs_value value, - char *buf, - ptrdiff_t *len) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - /* Create a Lisp string from a utf8 encoded string. */ - emacs_value (*make_string) (emacs_env *env, - const char *str, ptrdiff_t len) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Embedded pointer type. */ - emacs_value (*make_user_ptr) (emacs_env *env, - void (*fin) (void *) EMACS_NOEXCEPT, - void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void *(*get_user_ptr) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr)) - (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_finalizer) (emacs_env *env, emacs_value arg, - void (*fin) (void *) EMACS_NOEXCEPT) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Vector functions. */ - emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index, - emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Returns whether a quit is pending. */ - bool (*should_quit) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Processes pending input events and returns whether the module - function should quit. */ - enum emacs_process_input_result (*process_input) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL (1); - - struct timespec (*extract_time) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL (1); - - emacs_value (*make_time) (emacs_env *env, struct timespec time) - EMACS_ATTRIBUTE_NONNULL (1); - - bool (*extract_big_integer) (emacs_env *env, emacs_value arg, int *sign, - ptrdiff_t *count, emacs_limb_t *magnitude) - EMACS_ATTRIBUTE_NONNULL (1); - - emacs_value (*make_big_integer) (emacs_env *env, int sign, ptrdiff_t count, - const emacs_limb_t *magnitude) - EMACS_ATTRIBUTE_NONNULL (1); -}; - -struct emacs_env_28 -{ - /* Structure size (for version checking). */ - ptrdiff_t size; - - /* Private data; users should not touch this. */ - struct emacs_env_private *private_members; - - /* Memory management. */ - - emacs_value (*make_global_ref) (emacs_env *env, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*free_global_ref) (emacs_env *env, emacs_value global_value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Non-local exit handling. */ - - enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_clear) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - enum emacs_funcall_exit (*non_local_exit_get) - (emacs_env *env, emacs_value *symbol, emacs_value *data) - EMACS_ATTRIBUTE_NONNULL(1, 2, 3); - - void (*non_local_exit_signal) (emacs_env *env, - emacs_value symbol, emacs_value data) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*non_local_exit_throw) (emacs_env *env, - emacs_value tag, emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Function registration. */ - - emacs_value (*make_function) (emacs_env *env, - ptrdiff_t min_arity, - ptrdiff_t max_arity, - emacs_value (*func) (emacs_env *env, - ptrdiff_t nargs, - emacs_value* args, - void *data) - EMACS_NOEXCEPT - EMACS_ATTRIBUTE_NONNULL(1), - const char *docstring, - void *data) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - emacs_value (*funcall) (emacs_env *env, - emacs_value func, - ptrdiff_t nargs, - emacs_value* args) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*intern) (emacs_env *env, const char *name) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Type conversion. */ - - emacs_value (*type_of) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*is_not_nil) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - bool (*eq) (emacs_env *env, emacs_value a, emacs_value b) - EMACS_ATTRIBUTE_NONNULL(1); - - intmax_t (*extract_integer) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_integer) (emacs_env *env, intmax_t n) - EMACS_ATTRIBUTE_NONNULL(1); - - double (*extract_float) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - - emacs_value (*make_float) (emacs_env *env, double d) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Copy the content of the Lisp string VALUE to BUFFER as an utf8 - null-terminated string. - - SIZE must point to the total size of the buffer. If BUFFER is - NULL or if SIZE is not big enough, write the required buffer size - to SIZE and return true. - - Note that SIZE must include the last null byte (e.g. "abc" needs - a buffer of size 4). - - Return true if the string was successfully copied. */ - - bool (*copy_string_contents) (emacs_env *env, - emacs_value value, - char *buf, - ptrdiff_t *len) - EMACS_ATTRIBUTE_NONNULL(1, 4); - - /* Create a Lisp string from a utf8 encoded string. */ - emacs_value (*make_string) (emacs_env *env, - const char *str, ptrdiff_t len) - EMACS_ATTRIBUTE_NONNULL(1, 2); - - /* Embedded pointer type. */ - emacs_value (*make_user_ptr) (emacs_env *env, - void (*fin) (void *) EMACS_NOEXCEPT, - void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void *(*get_user_ptr) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr)) - (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1); - void (*set_user_finalizer) (emacs_env *env, emacs_value arg, - void (*fin) (void *) EMACS_NOEXCEPT) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Vector functions. */ - emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index) - EMACS_ATTRIBUTE_NONNULL(1); - - void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index, - emacs_value value) - EMACS_ATTRIBUTE_NONNULL(1); - - ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Returns whether a quit is pending. */ - bool (*should_quit) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL(1); - - /* Processes pending input events and returns whether the module - function should quit. */ - enum emacs_process_input_result (*process_input) (emacs_env *env) - EMACS_ATTRIBUTE_NONNULL (1); - - struct timespec (*extract_time) (emacs_env *env, emacs_value arg) - EMACS_ATTRIBUTE_NONNULL (1); - - emacs_value (*make_time) (emacs_env *env, struct timespec time) - EMACS_ATTRIBUTE_NONNULL (1); - - bool (*extract_big_integer) (emacs_env *env, emacs_value arg, int *sign, - ptrdiff_t *count, emacs_limb_t *magnitude) - EMACS_ATTRIBUTE_NONNULL (1); - - emacs_value (*make_big_integer) (emacs_env *env, int sign, ptrdiff_t count, - const emacs_limb_t *magnitude) - EMACS_ATTRIBUTE_NONNULL (1); - - /* Add module environment functions newly added in Emacs 28 here. - Before Emacs 28 is released, remove this comment and start - module-env-29.h on the master branch. */ - - void (*(*EMACS_ATTRIBUTE_NONNULL (1) - get_function_finalizer) (emacs_env *env, - emacs_value arg)) (void *) EMACS_NOEXCEPT; - - void (*set_function_finalizer) (emacs_env *env, emacs_value arg, - void (*fin) (void *) EMACS_NOEXCEPT) - EMACS_ATTRIBUTE_NONNULL (1); - - int (*open_channel) (emacs_env *env, emacs_value pipe_process) - EMACS_ATTRIBUTE_NONNULL (1); - - void (*make_interactive) (emacs_env *env, emacs_value function, - emacs_value spec) - EMACS_ATTRIBUTE_NONNULL (1); - - /* Create a unibyte Lisp string from a string. */ - emacs_value (*make_unibyte_string) (emacs_env *env, - const char *str, ptrdiff_t len) - EMACS_ATTRIBUTE_NONNULL(1, 2); -}; - -/* Every module should define a function as follows. */ -extern int emacs_module_init (struct emacs_runtime *runtime) - EMACS_NOEXCEPT - EMACS_ATTRIBUTE_NONNULL (1); - -#ifdef __cplusplus -} -#endif - -#endif /* EMACS_MODULE_H */ diff --git a/org/xeft/module/xapian-lite-internal.h b/org/xeft/module/xapian-lite-internal.h deleted file mode 100644 index d739004..0000000 --- a/org/xeft/module/xapian-lite-internal.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef XAPIAN_LITE_INTERNAL_H -#define XAPIAN_LITE_INTERNAL_H - -#include "emacs-module.h" - -typedef emacs_value (*emacs_subr) (emacs_env *env, - ptrdiff_t nargs, emacs_value *args, - void *data); -#ifdef __cplusplus -extern "C" { -#endif - -void -define_error -(emacs_env *env, const char *name, - const char *description, const char *parent); - -emacs_value -Fxapian_lite_reindex_file -(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) - EMACS_NOEXCEPT; - -emacs_value -Fxapian_lite_query_term -(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) - EMACS_NOEXCEPT; - -void -define_function -(emacs_env *env, const char *name, ptrdiff_t min_arity, - ptrdiff_t max_arity, emacs_subr function, const char *documentation); - -void -provide (emacs_env *env, const char *feature); - -#ifdef __cplusplus -} -#endif - -#endif /* XAPIAN_LITE_INTERNAL_H */ diff --git a/org/xeft/module/xapian-lite.cc b/org/xeft/module/xapian-lite.cc deleted file mode 100644 index 0ff505f..0000000 --- a/org/xeft/module/xapian-lite.cc +++ /dev/null @@ -1,446 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "emacs-module.h" -#include "emacs-module-prelude.h" - -using namespace std; - -__declspec(dllexport) int plugin_is_GPL_compatible; - -#if defined __cplusplus && __cplusplus >= 201103L -# define EMACS_NOEXCEPT noexcept -#else -# define EMACS_NOEXCEPT -#endif - -#define CHECK_EXIT(env) \ - if (env->non_local_exit_check (env) \ - != emacs_funcall_exit_return) \ - { return NULL; } - -/* A few notes: The database we use, WritableDatabase, will not throw - DatabaseModifiedError, so we don’t need to handle that. For query, - we first try to parse it with special syntax enabled, i.e., with - AND, OR, +/-, etc. If that doesn’t parse, we’ll just parse it as - plain text. - - REF: https://lists.xapian.org/pipermail/xapian-discuss/2021-August/009906.html - */ - -/*** Xapian stuff */ - -static const Xapian::valueno DOC_MTIME = 0; -static const Xapian::valueno DOC_FILEPATH = 1; - -static Xapian::WritableDatabase database; -static string cached_dbpath = ""; - -class xapian_lite_cannot_open_file: public exception {}; - -// Reindex the file at PATH, using database at DBPATH. Throws -// cannot_open_file. Both path must be absolute. Normally only reindex -// if file has change since last index, if FORCE is true, always -// reindex. Return true if re-indexed, return false if didn’t. -// LANG is the language used by the stemmer. -// Possible langauges: -// https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html -static bool -reindex_file -(string path, string dbpath, string lang = "en", bool force = false) -{ - // Check for mtime. - struct stat st; - time_t file_mtime; - off_t file_size; - if (stat (path.c_str(), &st) == 0) - { - file_mtime = st.st_mtime; - file_size = st.st_size; - } - else - { - throw xapian_lite_cannot_open_file(); - } - - // Even though the document says that database object only carries a - // pointer to the actual object, it is still not cheap enough. By - // using this cache, we get much better performance when reindexing - // hundreds of files, which most are no-op because they hasn’t been - // modified. - if (dbpath != cached_dbpath) - { - database = Xapian::WritableDatabase - (dbpath, Xapian::DB_CREATE_OR_OPEN); - cached_dbpath = dbpath; - } - // Track doc with file path as "id". See - // https://getting-started-with-xapian.readthedocs.io/en/latest/practical_example/indexing/updating_the_database.html - string termID = 'Q' + path; - Xapian::PostingIterator it_begin = database.postlist_begin (termID); - Xapian::PostingIterator it_end = database.postlist_end (termID); - bool has_doc = it_begin != it_end; - time_t db_mtime; - if (has_doc) - { - // sortable_serialise is for double and we can’t really use it. - Xapian::Document db_doc = database.get_document(*it_begin); - db_mtime = (time_t) stoi (db_doc.get_value (DOC_MTIME)); - } - - // Need re-index. - if (!has_doc || (has_doc && db_mtime < file_mtime) || force) - { - // Get the file content. - // REF: https://stackoverflow.com/questions/2912520/read-file-contents-into-a-string-in-c - ifstream infile (path); - string content ((istreambuf_iterator(infile)), - (istreambuf_iterator())); - // Create the indexer. - Xapian::TermGenerator indexer; - Xapian::Stem stemmer (lang); - indexer.set_stemmer (stemmer); - indexer.set_stemming_strategy - (Xapian::TermGenerator::STEM_SOME); - // Support CJK. - indexer.set_flags (Xapian::TermGenerator::FLAG_CJK_NGRAM); - // Index file content. - Xapian::Document new_doc; - indexer.set_document (new_doc); - indexer.index_text (content); - // Set doc info. - new_doc.add_boolean_term (termID); - // We store the path in value, no need to use set_data. - new_doc.add_value (DOC_FILEPATH, path); - new_doc.add_value (DOC_MTIME, (string) to_string (file_mtime)); - database.replace_document (termID, new_doc); - return true; - } - else - { - return false; - } -} - -// Query TERM in the databse at DBPATH. OFFSET and PAGE_SIZE is for -// paging, see the docstring for the lisp function. If a file in the -// result doesn’t exist anymore, it is removed from the database. -// LANG is the language used by the stemmer. -// Possible langauges: -// https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html -static vector -query_term -(string term, string dbpath, int offset, int page_size, - string lang = "en") -{ - // See reindex_file for the reason for caching the database object. - if (dbpath != cached_dbpath) - { - database = Xapian::WritableDatabase - (dbpath, Xapian::DB_CREATE_OR_OPEN); - cached_dbpath = dbpath; - } - - Xapian::QueryParser parser; - Xapian::Stem stemmer (lang); - parser.set_stemmer (stemmer); - parser.set_stemming_strategy (Xapian::QueryParser::STEM_SOME); - // Partial match (FLAG_PARTIAL) needs the database to expand - // wildcards. - parser.set_database(database); - - Xapian::Query query; - try - { - query = parser.parse_query - // CJK_NGRAM is the flag for CJK support. PARTIAL makes - // interactive search more stable. DEFAULT enables AND OR and - // +/-. - (term, Xapian::QueryParser::FLAG_CJK_NGRAM - | Xapian::QueryParser::FLAG_PARTIAL - | Xapian::QueryParser::FLAG_DEFAULT); - } - // If the syntax is syntactically wrong, Xapian throws this error. - // Try again without enabling any special syntax. - catch (Xapian::QueryParserError &e) - { - query = parser.parse_query - (term, Xapian::QueryParser::FLAG_CJK_NGRAM - | Xapian::QueryParser::FLAG_PARTIAL); - } - - Xapian::Enquire enquire (database); - enquire.set_query (query); - - Xapian::MSet mset = enquire.get_mset (offset, page_size); - vector result (0); - for (Xapian::MSetIterator it = mset.begin(); it != mset.end(); it++) - { - Xapian::Document doc = it.get_document(); - string path = doc.get_value(DOC_FILEPATH); - // If the file doesn’t exists anymore, remove it. - struct stat st; - if (stat (path.c_str(), &st) == 0) - { - result.push_back (doc.get_value (DOC_FILEPATH)); - } - else - { - database.delete_document (doc.get_docid()); - } - } - return result; -} - -/*** Module definition */ - -static string -copy_string (emacs_env *env, emacs_value value) -{ - char* char_buffer; - size_t size; - if (emp_copy_string_contents (env, value, &char_buffer, &size)) - { - string str = (string) char_buffer; - free (char_buffer); - return str; - } - else - { - emp_signal_message1 (env, "xapian-lite-error", - "Error turning lisp string to C++ string"); - return ""; - } -} - -static bool -NILP (emacs_env *env, emacs_value val) -{ - return !env->is_not_nil (env, val); -} - -static const char* xapian_lite_reindex_file_doc = - "Refindex file at PATH with database at DBPATH\n" - "Both paths has to be absolute. Normally, this function only\n" - "reindex a file if it has been modified since last indexed,\n" - "but if FORCE is non-nil, this function will always reindex.\n" - "Return non-nil if actually reindexed the file, return nil if not.\n" - "\n" - "LANG is the language used by the indexer, it tells Xapian how to\n" - "reduce words to word stems, e.g., apples <-> apple.\n" - "A full list of possible languages can be found at\n" - "https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html.\n" - "By default, LANG is \"en\".\n" - "\n" - "(fn PATH DBPATH &optional LANG FORCE)"; - -static emacs_value -Fxapian_lite_reindex_file -(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) - EMACS_NOEXCEPT -{ - - // Decode arguments. - emacs_value lisp_path = args[0]; - emacs_value lisp_dbpath = args[1]; - - if (NILP (env, emp_funcall (env, "file-name-absolute-p", 1, lisp_path))) - { - emp_signal_message1 (env, "xapian-lite-file-error", - "PATH is not a absolute path"); - return NULL; - } - if (NILP (env, - emp_funcall (env, "file-name-absolute-p", 1, lisp_dbpath))) - { - emp_signal_message1 (env, "xapian-lite-file-error", - "DBPATH is not a absolute path"); - return NULL; - } - - // Expand "~" in the filename. - emacs_value lisp_args[] = {lisp_path}; - lisp_path = emp_funcall (env, "expand-file-name", 1, lisp_path); - lisp_dbpath = emp_funcall (env, "expand-file-name", 1, lisp_dbpath); - - emacs_value lisp_lang = nargs < 3 ? emp_intern (env, "nil") : args[2]; - emacs_value lisp_force = nargs < 4 ? emp_intern (env, "nil") : args[3]; - - string path = copy_string (env, lisp_path); - string dbpath = copy_string (env, lisp_dbpath); - bool force = !NILP (env, lisp_force); - CHECK_EXIT (env); - string lang = NILP (env, lisp_lang) ? - "en" : copy_string (env, lisp_lang); - CHECK_EXIT (env); - - // Do the work. - bool indexed; - try - { - indexed = reindex_file (path, dbpath, lang, force); - return indexed ? emp_intern (env, "t") : emp_intern (env, "nil"); - } - catch (xapian_lite_cannot_open_file &e) - { - emp_signal_message1 (env, "xapian-lite-file-error", - "Cannot open the file"); - return NULL; - } - catch (Xapian::DatabaseCorruptError &e) - { - emp_signal_message1 (env, "xapian-lite-database-corrupt-error", - e.get_description().c_str()); - return NULL; - } - catch (Xapian::DatabaseLockError &e) - { - emp_signal_message1 (env, "xapian-lite-database-lock-error", - e.get_description().c_str()); - return NULL; - } - catch (Xapian::Error &e) - { - emp_signal_message1 (env, "xapian-lite-lib-error", - e.get_description().c_str()); - return NULL; - } - catch (exception &e) - { - emp_signal_message1 (env, "xapian-lite-error", - "Something went wrong"); - return NULL; - } -} - -static const char *xapian_lite_query_term_doc = - "Query for TERM in database at DBPATH.\n" - "Paging is supported by OFFSET and PAGE-SIZE. OFFSET specifies page\n" - "start, and PAGE-SIZE the size. For example, if a page is 10 entries,\n" - "OFFSET and PAGE-SIZE would be first 0 and 10, then 10 and 10, and\n" - "so on.\n" - "\n" - "If a file in the result doesn't exist anymore, it is removed from\n" - "the database, and is not included in the return value.\n" - "\n" - "LANG is the language used by the indexer, it tells Xapian how to\n" - "reduce words to word stems, e.g., apples <-> apple.\n" - "A full list of possible languages can be found at\n" - "https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html.\n" - "By default, LANG is \"en\".\n" - "\n" - "TERM can use common Xapian syntax like AND, OR, and +/-.\n" - "Specifically, this function supports:\n" - "\n" - " Boolean operators: AND, OR, XOR, NOT\n" - " Parenthesized expression: ()\n" - " Love/hate terms: +/-\n" - " Exact match: \"\"\n" - "\n" - "If TERM contains syntactic errors, like \"a AND AND b\",\n" - "it is treated as a plain term.\n" - "\n" - "(fn TERM DBPATH OFFSET PAGE-SIZE &optional LANG)"; - -static emacs_value -Fxapian_lite_query_term -(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) - EMACS_NOEXCEPT -{ - // Decode arguments. - emacs_value lisp_term = args[0]; - emacs_value lisp_dbpath = args[1]; - emacs_value lisp_offset = args[2]; - emacs_value lisp_page_size = args[3]; - - if (NILP (env, - emp_funcall (env, "file-name-absolute-p", 1, lisp_dbpath))) - { - emp_signal_message1 (env, "xapian-lite-file-error", - "DBPATH is not a absolute path"); - return NULL; - } - - lisp_dbpath = emp_funcall (env, "expand-file-name", 1, lisp_dbpath); - - string term = copy_string (env, lisp_term); - string dbpath = copy_string (env, lisp_dbpath); - int offset = env->extract_integer (env, lisp_offset); - int page_size = env->extract_integer (env, lisp_page_size); - CHECK_EXIT (env); - - vector result; - try - { - result = query_term (term, dbpath, offset, page_size); - } - catch (Xapian::Error &e) - { - emp_signal_message1 (env, "xapian-lite-lib-error", - e.get_description().c_str()); - return NULL; - } - catch (exception &e) - { - emp_signal_message1 (env, "xapian-lite-error", - "Something went wrong"); - return NULL; - } - - vector::iterator it; - emacs_value ret = emp_intern (env, "nil"); - for (it = result.begin(); it != result.end(); it++) { - ret = emp_funcall (env, "cons", 2, - env->make_string - (env, it->c_str(), strlen(it->c_str())), - ret); - CHECK_EXIT (env); - } - return emp_funcall (env, "reverse", 1, ret); -} - -int __declspec(dllexport) -emacs_module_init (struct emacs_runtime *ert) EMACS_NOEXCEPT -{ - emacs_env *env = ert->get_environment (ert); - - emp_define_error (env, "xapian-lite-error", - "Generic xapian-lite error", "error"); - emp_define_error (env, "xapian-lite-lib-error", - "Xapian library error", "xapian-lite-error"); - emp_define_error (env, "xapian-lite-database-corrupt-error", - "Xapian library error", "xapian-lite-lib-error"); - emp_define_error (env, "xapian-lite-database-lock-error", - "Xapian library error", "xapian-lite-lib-error"); - emp_define_error (env, "xapian-lite-file-error", - "Cannot open file", "xapian-lite-error"); - - emp_define_function(env, "xapian-lite-reindex-file", 2, 3, - &Fxapian_lite_reindex_file, - xapian_lite_reindex_file_doc); - emp_define_function(env, "xapian-lite-query-term", 4, 4, - &Fxapian_lite_query_term, - xapian_lite_query_term_doc); - - emp_provide (env, "xapian-lite"); - - /* Return 0 to indicate module loaded successfully. */ - return 0; -} diff --git a/org/xeft/xapian-lite.dll b/org/xeft/xapian-lite.dll deleted file mode 100644 index a3a5a4e6783a6c82cb68f5fed4bc85c00325941d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249430 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 NOfReUQR$6U{~vq;-n9S# diff --git a/org/xeft/xeft.el b/org/xeft/xeft.el deleted file mode 100644 index cf346f8..0000000 --- a/org/xeft/xeft.el +++ /dev/null @@ -1,760 +0,0 @@ -;;; xeft.el --- Deft feat. Xapian -*- lexical-binding: t; -*- - -;; Author: Yuan Fu - -;;; This file is NOT part of GNU Emacs - -;;; Commentary: -;; -;; Usage: -;; -;; Type M-x xeft RET, and you should see the Xeft buffer. Type in your -;; search phrase in the first line and the results will show up as you -;; type. Press C-n and C-p to go through each file. You can preview a -;; file by pressing SPC when the point is on a file, or click the file -;; with the mouse. Press RET to open the file in the same window. -;; -;; Type C-c C-g to force a refresh. When point is on the search -;; phrase, press RET to create a file with the search phrase as -;; the filename and title. -;; -;; Note that: -;; -;; 1. Xeft only looks for first-level files in ‘xeft-directory’. Files -;; in sub-directories are not searched unless ‘xeft-recursive’ is -;; non-nil. -;; -;; 2. Xeft creates a new file by using the search phrase as the -;; filename and title. If you want otherwise, redefine -;; ‘xeft-create-note’ or ‘xeft-filename-fn’. -;; -;; 3. Xeft saves the current window configuration before switching to -;; Xeft buffer. When Xeft buffer is killed, Xeft restores the saved -;; window configuration. -;; -;; On search queries: -;; -;; Since Xeft uses Xapian, it supports the query syntax Xapian -;; supports: -;; -;; AND, NOT, OR, XOR and parenthesizes -;; +word1 -word2 which matches documents that contains WORD1 but not -;; WORD2. -;; word1 NEAR word2 which matches documents in where word1 is near word2. -;; word1 ADJ word2 which matches documents in where word1 is near word2 -;; and word1 comes before word2 -;; "word1 word2" which matches exactly “word1 word2” -;; -;; Xeft deviates from Xapian in one aspect: consecutive phrases have -;; implied “AND” between them. So "word1 word2 word3" is actually seen -;; as "word1 AND word2 AND word3". See ‘xeft--tighten-search-phrase’ -;; for how exactly is it done. -;; -;; See https://xapian.org/docs/queryparser.html for Xapian’s official -;; documentation on query syntax. - -;;; Code: - -(require 'cl-lib) -(declare-function xapian-lite-reindex-file nil - (path dbpath &optional lang force)) -(declare-function xapian-lite-query-term nil - (term dbpath offset page-size &optional lang)) - -;;; Customize - -(defgroup xeft nil - "Xeft note interface." - :group 'applications) - -(defcustom xeft-directory "~/.deft" - "Directory in where notes are stored. Must be a full path." - :type 'directory) - -(defcustom xeft-database "~/.deft/db" - "The path to the database." - :type 'directory) - -(defcustom xeft-find-file-hook nil - "Hook run when Xeft opens a file." - :type 'hook) - -(defface xeft-selection - '((t . (:inherit region :extend t))) - "Face for the current selected search result.") - -(defface xeft-inline-highlight - '((t . (:inherit underline :extend t))) - "Face for inline highlighting in Xeft buffer.") - -(defface xeft-preview-highlight - '((t . (:inherit highlight :extend t))) - "Face for highlighting in the preview buffer.") - -(defface xeft-excerpt-title - '((t . (:inherit (bold underline)))) - "Face for the excerpt title.") - -(defface xeft-excerpt-body - '((t . (:inherit default))) - "Face for the excerpt body.") - -(defcustom xeft-default-extension "txt" - "The default extension for new files created by xeft." - :type 'string) - -(defcustom xeft-filename-fn - (lambda (search-phrase) - (concat search-phrase "." xeft-default-extension)) - "A function that takes the search phrase and returns a filename." - :type 'function) - -(defcustom xeft-ignore-extension '("iimg") - "Files with extensions in this list are ignored. - -To remove the files that you want to ignore but are already -indexed in the database, simply delete the database and start -xeft again." - :type '(list string)) - -(defcustom xeft-recursive nil - "If non-nil, xeft searches for file recursively. -Xeft doesn’t follow symlinks and ignores inaccessible directories." - :type 'boolean) - -(defcustom xeft-file-list-function #'xeft--file-list - "A function that returns files that xeft should search from. -This function takes no arguments and return a list of absolute paths." - :type 'function) - -;;; Compile - -(defun xeft--compile-module () - "Compile the dynamic module. Return non-nil if success." - ;; Just following vterm.el here. - (when (not (executable-find "make")) - (user-error "Couldn’t compile xeft: cannot find make")) - (let* ((source-dir - (shell-quote-argument - (file-name-directory - (locate-library "xeft.el" t)))) - (command (format "cd %s; make PREFIX=%s" - source-dir - (read-string "PREFIX (empty by default): "))) - (buffer (get-buffer-create "*xeft compile*"))) - (if (zerop (let ((inhibit-read-only t)) - (call-process "sh" nil buffer t "-c" command))) - (progn (message "Successfully compiled the module :-D") t) - (pop-to-buffer buffer) - (compilation-mode) - (message "Failed to compile the module") - nil))) - -(defvar xeft--linux-module-url "https://github.com/casouri/xapian-lite/releases/download/v1.0/xapian-lite-amd64-linux.so" - "URL for pre-built dynamic module for Linux.") - -(defvar xeft--mac-module-url "https://github.com/casouri/xapian-lite/releases/download/v1.0/xapian-lite-amd64-mac.dylib" - "URL for pre-built dynamic module for Mac.") - -(defun xeft--download-module () - "Download pre-built module from GitHub. Return non-nil if success." - (require 'url) - (let ((module-path (expand-file-name - "xapian-lite.so" - (file-name-directory - (locate-library "xeft.el" t))))) - (cond - ((eq system-type 'gnu/linux) - (url-copy-file xeft--linux-module-url module-path) - t) - ((eq system-type 'darwin) - (url-copy-file xeft--mac-module-url module-path) - t) - (t (message "No pre-built module for this operating system. We only have them for GNU/Linux and macOS") - nil)))) - -;;; Helpers - -(defvar xeft--last-window-config nil - "Window configuration before Xeft starts.") - -(defun xeft--buffer () - "Return the xeft buffer." - (get-buffer-create "*xeft*")) - -(defun xeft--work-buffer () - "Return the work buffer for Xeft. Used for holding file contents." - (get-buffer-create " *xeft work*")) - -(defun xeft--after-save () - "Reindex the file." - (condition-case _ - (xapian-lite-reindex-file (buffer-file-name) xeft-database) - (xapian-lite-database-lock-error - (message "The Xeft database is locked (maybe there is another Xeft instance running) so we will skip indexing this file for now")) - (xapian-lite-database-corrupt-error - (message "The Xeft database is corrupted! You should delete the database and Xeft will recreate it. Make sure other programs are not messing with Xeft database")))) - -(defvar xeft-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") #'xeft-create-note) - (define-key map (kbd "C-c C-g") #'xeft-refresh-full) - (define-key map (kbd "C-c C-r") #'xeft-full-reindex) - (define-key map (kbd "C-n") #'xeft-next) - (define-key map (kbd "C-p") #'xeft-previous) - map) - "Mode map for `xeft-mode'.") - -(defvar xeft--need-refresh) -(define-derived-mode xeft-mode fundamental-mode - "Xeft" "Search for notes and display summaries." - (let ((inhibit-read-only t)) - (visual-line-mode) - (setq default-directory xeft-directory - xeft--last-window-config (current-window-configuration)) - ;; Hook ‘after-change-functions’ is too primitive, binding to that - ;; will cause problems with electric-pairs. - (add-hook 'post-command-hook - (lambda (&rest _) - (when xeft--need-refresh - (let ((inhibit-modification-hooks t)) - ;; We don’t want ‘after-change-functions’ to run - ;; when we refresh the buffer, because we set - ;; ‘xeft--need-refresh’ in that hook. - (xeft-refresh)))) - 0 t) - (add-hook 'after-change-functions - (lambda (&rest _) (setq xeft--need-refresh t)) 0 t) - (add-hook 'window-size-change-functions - (lambda (&rest _) (xeft-refresh)) 0 t) - (add-hook 'kill-buffer-hook - (lambda () - (when xeft--last-window-config - (set-window-configuration xeft--last-window-config))) - 0 t) - (erase-buffer) - (insert "\n\nInsert search phrase and press RET to search.") - (goto-char (point-min)))) - - -;;; Userland - -;;;###autoload -(defun xeft () - "Start Xeft." - (interactive) - (when (not (file-name-absolute-p xeft-directory)) - (user-error "XEFT-DIRECTORY must be an absolute path")) - (when (not (file-exists-p xeft-directory)) - (mkdir xeft-directory t)) - (when (not (file-name-absolute-p xeft-database)) - (user-error "XEFT-DATABASE must be an absolute path")) - (when (not (file-exists-p xeft-database)) - (mkdir xeft-database t)) - (unless (require 'xapian-lite nil t) - ;; I can hide download option for non-Linux/mac users, but I’m - ;; lazy. - (let* ((choice (read-char (concat - "Xeft needs the dynamic module to work, " - "download pre-built module " - (propertize "[b]" 'face 'bold) - ", compile locally " - (propertize "[c]" 'face 'bold) - ", or give up " - (propertize "[q]" 'face 'bold) - "?"))) - (success (cond ((eq choice ?b) - (xeft--download-module)) - ((eq choice ?c) - (xeft--compile-module)) - (t nil)))) - (when success - (require 'xapian-lite)))) - (if (not (featurep 'xapian-lite)) - (message "Since there is no require dynamic module, we can’t start Xeft") - (setq xeft--last-window-config (current-window-configuration)) - (switch-to-buffer (xeft--buffer)) - (when (not (derived-mode-p 'xeft-mode)) - (xeft-mode)) - ;; Reindex all files. We reindex every time M-x xeft is called. - ;; Because sometimes I use other functions to move between files, - ;; edit them, and come back to Xeft buffer to search. By that time - ;; some file are changed without Xeft noticing. - (xeft-full-reindex) - ;; Also regenerate newest file cache, for the same reason as above. - (xeft--front-page-cache-refresh))) - -(defun xeft-create-note () - "Create a new note with the current search phrase as the title." - (interactive) - (let* ((search-phrase (xeft--get-search-phrase)) - (file-name (funcall xeft-filename-fn search-phrase)) - (file-path (expand-file-name file-name xeft-directory)) - (exists-p (file-exists-p file-path))) - ;; If there is no match, create the file without confirmation, - ;; otherwise prompt for confirmation. NOTE: this is not DRY, but - ;; should be ok. - (when (or (search-forward "Press RET to create a new note" nil t) - (y-or-n-p (format "Create file `%s'? " file-name))) - (find-file file-path) - (unless exists-p - (insert search-phrase "\n\n") - (save-buffer) - ;; This should cover most cases. - (xeft--front-page-cache-refresh)) - (run-hooks 'xeft-find-file-hook)))) - -(defvar-local xeft--select-overlay nil - "Overlay used for highlighting selected search result.") - -(defun xeft--highlight-file-at-point () - "Activate (highlight) the file excerpt button at point." - (when-let ((button (button-at (point)))) - ;; Create the overlay if it doesn't exist yet. - (when (null xeft--select-overlay) - (setq xeft--select-overlay (make-overlay (button-start button) - (button-end button))) - (overlay-put xeft--select-overlay 'evaporate t) - (overlay-put xeft--select-overlay 'face 'xeft-selection)) - ;; Move the overlay over the file. - (move-overlay xeft--select-overlay - (button-start button) (button-end button)))) - -(defun xeft-next () - "Move to next file excerpt." - (interactive) - (when (forward-button 1 nil nil t) - (xeft--highlight-file-at-point))) - -(defun xeft-previous () - "Move to previous file excerpt." - (interactive) - (if (backward-button 1 nil nil t) - (xeft--highlight-file-at-point) - ;; Go to the end of the search phrase. - (goto-char (point-min)) - (end-of-line))) - -(defun xeft-full-reindex () - "Do a full reindex of all files." - (interactive) - (condition-case _ - (dolist (file (funcall xeft-file-list-function)) - (xapian-lite-reindex-file file xeft-database)) - (xapian-lite-database-lock-error - (message "The Xeft database is locked (maybe there is another Xeft instance running) so we will skip indexing for now")) - (xapian-lite-database-corrupt-error - (message "The Xeft database is corrupted! You should delete the database and Xeft will recreate it. Make sure other programs are not messing with Xeft database")))) - -;;; Draw - -(defvar xeft--preview-window nil - "Xeft shows file previews in this window.") - -(defun xeft--get-search-phrase () - "Return the search phrase. Assumes current buffer is a xeft buffer." - (save-excursion - (goto-char (point-min)) - (string-trim - (buffer-substring-no-properties (point) (line-end-position))))) - -(defun xeft--find-file-at-point () - "View file at point." - (interactive) - (find-file (button-get (button-at (point)) 'path)) - (run-hooks 'xeft-find-file-hook) - (add-hook 'after-save-hook #'xeft--after-save 0 t)) - -(defun xeft--preview-file (file &optional select) - "View FILE in another window. -If SELECT is non-nil, select the buffer after displaying it." - (interactive) - (let* ((buffer (find-file-noselect file)) - (search-phrase (xeft--get-search-phrase)) - (keyword-list (split-string search-phrase))) - (if (and (window-live-p xeft--preview-window) - (not (eq xeft--preview-window (selected-window)))) - (with-selected-window xeft--preview-window - (switch-to-buffer buffer)) - (setq xeft--preview-window - (display-buffer - buffer '((display-buffer-use-some-window - display-buffer-in-direction - display-buffer-pop-up-window) - . ((inhibit-same-window . t) - (direction . right) - (window-width - . (lambda (win) - (let ((width (window-width))) - (when (< width 50) - (window-resize - win (- 50 width) t)))))))))) - (if select (select-window xeft--preview-window)) - (with-current-buffer buffer - (xeft--highlight-matched keyword-list) - (run-hooks 'xeft-find-file-hook)))) - -(define-button-type 'xeft-excerpt - 'action (lambda (button) - ;; If the file is no already highlighted, highlight it - ;; first. - (when (not (and xeft--select-overlay - (overlay-buffer xeft--select-overlay) - (<= (overlay-start xeft--select-overlay) - (button-start button) - (overlay-end xeft--select-overlay)))) - (goto-char (button-start button)) - (xeft--highlight-file-at-point)) - (xeft--preview-file (button-get button 'path))) - 'keymap (let ((map (make-sparse-keymap))) - (set-keymap-parent map button-map) - (define-key map (kbd "RET") #'xeft--find-file-at-point) - (define-key map (kbd "SPC") #'push-button) - map) - 'help-echo "Open this file" - 'follow-link t - 'face 'default - 'mouse-face 'xeft-selection) - -(defun xeft--highlight-search-phrase () - "Highlight search phrases in buffer." - (let ((keyword-list (cl-remove-if - (lambda (word) - (or (member word '("OR" "AND" "XOR" "NOT" "NEAR")) - (string-prefix-p "ADJ" word))) - (split-string (xeft--get-search-phrase)))) - (inhibit-read-only t)) - (dolist (keyword keyword-list) - (when (> (length keyword) 1) - (goto-char (point-min)) - (forward-line 2) - ;; We use overlay because overlay allows face composition. - ;; So we can have bold + underline. - (while (search-forward keyword nil t) - (let ((ov (make-overlay (match-beginning 0) - (match-end 0)))) - (overlay-put ov 'face 'xeft-inline-highlight) - (overlay-put ov 'xeft-highlight t) - (overlay-put ov 'evaporate t))))))) - -(defvar xeft--ecache nil - "Cache for finding excerpt for a file.") - -(defun xeft--ecache-buffer (file) - "Return a buffer that has the content of FILE. -Doesn’t check for modification time, and not used." - (or (alist-get (sxhash file) xeft--ecache) - (progn - (let ((buf (get-buffer-create - (format " *xeft-ecache %s*" file)))) - (with-current-buffer buf - (setq buffer-undo-list t) - (insert-file-contents file nil nil nil t)) - (push (cons (sxhash file) buf) xeft--ecache) - (when (> (length xeft--ecache) 30) - (kill-buffer (cdr (nth 30 xeft--ecache))) - (setcdr (nthcdr 29 xeft--ecache) nil)) - buf)))) - -(defun xeft--insert-file-excerpt (file search-phrase) - "Insert an excerpt for FILE at point. -This excerpt contains note title and content excerpt and is -clickable. FILE should be an absolute path. SEARCH-PHRASE is the -search phrase the user typed." - (let ((excerpt-len (floor (* 2.7 (1- (window-width))))) - (last-search-term - (car (last (split-string search-phrase)))) - title excerpt) - (with-current-buffer (xeft--work-buffer) - (setq buffer-undo-list t) - ;; The times saved by caching is not significant enough. So I - ;; choose to not cache, but kept the code just in case. See - ;; ‘xeft--ecache-buffer’. - (insert-file-contents file nil nil nil t) - (goto-char (point-min)) - (search-forward "#+TITLE: " (line-end-position) t) - (let ((bol (point))) - (end-of-line) - (setq title (buffer-substring-no-properties bol (point)))) - (when (eq title "") (setq title "no title")) - (narrow-to-region (point) (point-max)) - ;; Grab excerpt. - (setq excerpt (string-trim - (replace-regexp-in-string - "[[:space:]]+" - " " - (if (and last-search-term - (search-forward last-search-term nil t)) - (buffer-substring-no-properties - (max (- (point) (/ excerpt-len 2)) - (point-min)) - (min (+ (point) (/ excerpt-len 2)) - (point-max))) - (buffer-substring-no-properties - (point) - (min (+ (point) excerpt-len) - (point-max)))))))) - ;; Now we insert the excerpt - (let ((start (point))) - (insert (propertize title 'face 'xeft-excerpt-title) - "\n" - (propertize excerpt 'face 'xeft-excerpt-body) - "\n\n") - ;; If we use overlay (with `make-button'), the button's face - ;; will override the bold and light face we specified above. - (make-text-button start (- (point) 2) - :type 'xeft-excerpt - 'path file)))) - -;;; Refresh and search - -(defun xeft-refresh-full () - "Refresh and display _all_ results." - (interactive) - (xeft-refresh t)) - -(defun xeft--file-list () - "Default function for ‘xeft-file-list-function’. -Return a list of all files in ‘xeft-directory’, ignoring dot -files and directories and check for ‘xeft-ignore-extension’." - (cl-remove-if-not - (lambda (file) - (and (file-regular-p file) - (not (string-prefix-p - "." (file-name-base file))) - (not (member (file-name-extension file) - xeft-ignore-extension)))) - (if xeft-recursive - (directory-files-recursively - xeft-directory "" nil (lambda (dir) - (not (string-prefix-p - "." (file-name-base dir))))) - (directory-files - xeft-directory t nil t)))) - -(defvar-local xeft--need-refresh t - "If change is made to the buffer, set this to t. -Once refreshed the buffer, set this to nil.") - -(defun xeft--tighten-search-phrase (phrase) - "Basically insert AND between each term in PHRASE." - (let ((lst (split-string phrase)) - (in-quote nil)) - ;; Basically we only insert AND between two normal phrases, and - ;; don’t insert if any of the two is an operator (AND, OR, +/-, - ;; etc), we also don’t insert AND in quoted phrases. - (string-join - (append (cl-loop for idx from 0 to (- (length lst) 2) - for this = (nth idx lst) - for next = (nth (1+ idx) lst) - collect this - if (and (not in-quote) (eq (aref this 0) ?\")) - do (setq in-quote t) - if (and in-quote - (eq (aref this (1- (length this))) ?\")) - do (setq in-quote nil) - if (not - (or in-quote - (member this '("AND" "NOT" "OR" "XOR" "NEAR")) - (string-prefix-p "ADJ" this) - (memq (aref this 0) '(?+ ?-)) - (member next '("AND" "NOT" "OR" "XOR" "NEAR")) - (string-prefix-p "ADJ" next) - (memq (aref next 0) '(?+ ?-)))) - collect "AND") - (last lst)) - " "))) - -;; This makes the integrative search results much more stable and -;; experience more fluid. And because we are not showing radically -;; different results from one key-press to another, the latency goes -;; down, I’m guessing because caching in CPU or RAM or OS or whatever. -(defun xeft--ignore-short-phrase (phrase) - "If the last term in PHRASE is too short, remove it." - (let* ((lst (or (split-string phrase) '(""))) - (last (car (last lst)))) - (if (and (not (string-match-p (rx (or (category chinese) - (category japanese) - (category korean))) - last)) - (< (length last) 3)) - (string-join (cl-subseq lst 0 (1- (length lst))) " ") - (string-join lst " ")))) - -;; See comment in ‘xeft-refresh’. -(defvar xeft--front-page-cache nil - "Stores the newest 15 or so files.") - -(defun xeft--front-page-cache-refresh () - "Refresh ‘xeft--front-page-cache’ and return it." - (setq xeft--front-page-cache - (cl-sort (funcall xeft-file-list-function) - #'file-newer-than-file-p))) - -(defun xeft-refresh (&optional full) - "Search for notes and display their summaries. -By default, only display the first 15 results. If FULL is -non-nil, display all results." - (interactive) - (when (derived-mode-p 'xeft-mode) - (let ((search-phrase (xeft--ignore-short-phrase - (xeft--get-search-phrase)))) - (let* ((phrase-empty (equal search-phrase "")) - (file-list nil) - (list-clipped nil)) - ;; 1. Get a list of files to show. - (setq file-list - ;; If the search phrase is empty (or too short and thus - ;; ignored), we show the newest files. - (if phrase-empty - (or xeft--front-page-cache - ;; Why cache? Turns out sorting this list by - ;; modification date is slow enough to be - ;; perceivable. - (setq xeft--front-page-cache - (xeft--front-page-cache-refresh))) - (xapian-lite-query-term - (xeft--tighten-search-phrase search-phrase) - xeft-database - ;; 16 is just larger than 15, so we will know it when - ;; there are more results. - 0 (if full 2147483647 16)))) - (when (and (null full) (> (length file-list) 15)) - (setq file-list (cl-subseq file-list 0 15) - list-clipped t)) - ;; 2. Display these files with excerpt. We do a - ;; double-buffering: first insert in a temp buffer, then - ;; insert the whole thing into this buffer. - (let ((inhibit-read-only t) - (orig-point (point)) - (new-content - (while-no-input - (with-temp-buffer - ;; Insert excerpts. - (if file-list - (dolist (file file-list) - (xeft--insert-file-excerpt - file search-phrase)) - ;; NOTE: this string is referred in - ;; ‘xeft-create-note’. - (unless phrase-empty - (insert "Press RET to create a new note"))) - ;; Insert clipped notice. - (when list-clipped - (insert - (format - "[Only showing the first 15 results, type %s to show all of them]\n" - (key-description - (where-is-internal #'xeft-refresh-full - xeft-mode-map t))))) - (buffer-string))))) - ;; 2.2 Actually insert the content. - (when (stringp new-content) - (while-no-input - (setq buffer-undo-list t) - (goto-char (point-min)) - (forward-line 2) - (let ((start (point))) - (delete-region (point) (point-max)) - (insert new-content) - (put-text-property (- start 2) (point) 'read-only t) - (xeft--highlight-search-phrase) - (set-buffer-modified-p nil) - ;; If finished, update this variable. - (setq xeft--need-refresh nil) - (buffer-enable-undo)))) - ;; Save excursion wouldn’t work since we erased the - ;; buffer and re-inserted contents. - (goto-char orig-point) - ;; Re-apply highlight. - (xeft--highlight-file-at-point)))))) - -;;; Highlight matched phrases - -(defun xeft--highlight-matched (keyword-list) - "Highlight keywords in KEYWORD-LIST in the current buffer." - (save-excursion - ;; Add highlight overlays. - (dolist (keyword keyword-list) - (when (> (length keyword) 1) - (goto-char (point-min)) - (while (search-forward keyword nil t) - (let ((ov (make-overlay (match-beginning 0) - (match-end 0)))) - (overlay-put ov 'face 'xeft-preview-highlight) - (overlay-put ov 'xeft-highlight t))))) - ;; Add cleanup hook. - (add-hook 'window-selection-change-functions - #'xeft--cleanup-highlight - 0 t))) - -(defun xeft--cleanup-highlight (window) - "Cleanup highlights in WINDOW." - (when (eq window (selected-window)) - (let ((ov-list (overlays-in (point-min) - (point-max)))) - (dolist (ov ov-list) - (when (overlay-get ov 'xeft-highlight) - (delete-overlay ov)))) - (remove-hook 'window-selection-change-functions - #'xeft--cleanup-highlight - t))) - -;;; Inferred links - -(defun xeft--extract-buffer-words (buffer) - "Extract words in BUFFER and return in a list. -Each element looks like (BEG . WORD) where BEG is the buffer -position of WORD." - (with-current-buffer buffer - (goto-char (point-min)) - (let (beg end word-list) - (while (progn (and (re-search-forward (rx word) nil t) - (setq beg (match-beginning 0)) - (re-search-forward (rx (not word)) nil t) - (setq end (match-beginning 0)))) - (push (cons beg (buffer-substring-no-properties beg end)) - word-list)) - (nreverse word-list)))) - -(defun xeft--generate-phrase-list (word-list max-len) - "Given WORD-LIST, generate all possible phrases up to MAX-LEN long. -Eg, given WORD-LIST = (a b c), len = 3, return - - ((a) (b) (c) (a b) (b c) (a b c))" - (cl-loop for len from 1 to max-len - append (cl-loop - for idx from 0 to (- (length word-list) len) - collect (cl-subseq word-list idx (+ idx len))))) - -(defun xeft--collect-inferred-links - (buffer max-len lower-bound upper-bound) - "Collect inferred links in BUFFER. -MAX-LEN is the same as in ‘xeft--generate-phrase-list’. Only -phrases with number of results between LOWER-BOUND and -UPPER-BOUND (inclusive) are collected." - (let* ((word-list (xeft--extract-buffer-words buffer)) - (phrase-list (xeft--generate-phrase-list - word-list max-len)) - (query-list (mapcar (lambda (phrase-list) - (let ((pos (caar phrase-list)) - (words (mapcar #'cdr phrase-list))) - (cons pos (concat "\"" - (string-join - words) - "\"")))) - phrase-list)) - (link-list - ;; QUERY-CONS = (POS . QUERY-TERM) - (cl-loop for query-cons in query-list - for file-list = (xapian-lite-query-term - (cdr query-cons) xeft-database - 0 (1+ upper-bound)) - if (<= lower-bound (length file-list) upper-bound) - collect (cons (cdr query-cons) - (length file-list))))) - link-list)) - -(provide 'xeft) - -;;; xeft.el ends here