├── ScreenShot.png ├── .github └── workflows │ └── melpazoid.yml ├── README.md ├── arxiv-query.el ├── LICENSE ├── arxiv-vars.el └── arxiv-mode.el /ScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fizban007/arxiv-mode/HEAD/ScreenShot.png -------------------------------------------------------------------------------- /.github/workflows/melpazoid.yml: -------------------------------------------------------------------------------- 1 | # melpazoid build checks. 2 | 3 | # If your package is on GitHub, enable melpazoid's checks by copying this file 4 | # to .github/workflows/melpazoid.yml and modifying RECIPE and EXIST_OK below. 5 | 6 | name: melpazoid 7 | on: [push, pull_request] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Python 3.9 15 | uses: actions/setup-python@v1 16 | with: { python-version: 3.9 } 17 | - name: Install 18 | run: | 19 | python -m pip install --upgrade pip 20 | sudo apt-get install emacs && emacs --version 21 | git clone https://github.com/riscy/melpazoid.git ~/melpazoid 22 | pip install ~/melpazoid 23 | - name: Run 24 | env: 25 | LOCAL_REPO: ${{ github.workspace }} 26 | # RECIPE is your recipe as written for MELPA: 27 | RECIPE: (arxiv-mode :repo "fizban007/arxiv-mode" :fetcher github) 28 | # set this to false (or remove it) if the package isn't on MELPA: 29 | EXIST_OK: true 30 | run: echo $GITHUB_REF && make -C ~/melpazoid 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/arxiv-mode-badge.svg)](https://melpa.org/#/arxiv-mode) [![MELPA Stable](https://stable.melpa.org/packages/arxiv-mode-badge.svg)](https://stable.melpa.org/#/arxiv-mode) 2 | 3 | # arXiv-mode 4 | 5 | arxiv-mode is an [Emacs](www.gnu.org/s/emacs/‎) major mode for viewing 6 | updates on [arXiv.org](http://arxiv.org). 7 | 8 | ![screenshot](ScreenShot.png) 9 | 10 | 11 | ## Common Usage 12 | 13 | arxiv-mode provides many functions for accessing [arXiv.org](http://arxiv.org). 14 | To browse the daily new submissions list in a category, run `M-x arxiv-read-new`. 15 | To browse the recent (weekly) submissions, run `M-x arxiv-read-recent`. 16 | Use `M-x arxiv-read-author` to search for specific author(s). 17 | Use `M-x arxiv-search` to perform a simple search on the arXiv database. 18 | 19 | For more complicated searches, use `M-x arxiv-complex-search`. 20 | This command allows user to dynamically refine and modify search conditions. 21 | You can also use `r` to refine search condition in the abstract list obtained from a search. 22 | 23 | In the article list, use `n` and `p` to navigate the article list. 24 | Press `SPC` to toggle visibility of the abstract window. 25 | Press `RET` to open the entry in a web browser. Press `d` to download the pdf. 26 | Press `b` to export the bibtex entry of current paper to your specified .bib file. 27 | Press `B` to export the bibtex entry to a new buffer. 28 | Press `e` to download pdf and add a bibtex entry with a link to the actual pdf file. 29 | 30 | All available commands are listed in a hydra help menu accessable by `?` whenever you are in the article list. 31 | 32 | ## Installation 33 | 34 | arxiv-mode is available on MELPA. 35 | After `M-x package-install RET arxiv-mode RET`, put the following code in your `init.el`: 36 | ````lisp 37 | (require 'arxiv-mode) 38 | ```` 39 | Or if you use `use-package`, you can simply put: 40 | ````lisp 41 | (use-package arxiv-mode 42 | :ensure t) 43 | ```` 44 | into the your init file. `use-package` will automatically download `arxiv-mode` for you. 45 | 46 | ## Customization 47 | 48 | Run `M-x arxiv-customize` to customize or set the customization variables directly: 49 | 50 | ### appearance 51 | The variable `arxiv-use-variable-pitch` decides whether to use monospace fonts or variable pitch fonts to display the contents of arxiv mode. 52 | You can also set the relevant faces (faces start with `arxiv-`) directly. 53 | The variable `arxiv-author-list-maximum` sets the maximum number of authors to display in the query list. 54 | 55 | ### behavior 56 | If you set `arxiv-startup-with-abstract-window` to `t`, arxiv-mode will default to startup with the abstract window open instead of a plain query list. 57 | The variable `arxiv-pop-up-new-frame` controls whether a new frame will be displayed for the query result. It defaults to `t`. 58 | You can specify the frame properties of the new frame by customizing the variable `arxiv-frame-alist`. 59 | The default category used for query can be set by changing the variable `arxiv-default-category`. 60 | The variable `arxiv-entries-per-fetch` sets the maximum number of articles arxiv-mode will try to query each time from arxiv.org (Note that setting this number too high will cause emacs to stutter). 61 | 62 | ### download and export 63 | By default, arxiv-mode will save downloaded PDFs to `~/Downloads` folder. You can change the folder by setting the variable `arxiv-default-download-folder`. 64 | It also defaults to open the PDFs inside emacs. Change `arxiv-pdf-open-function` if you want to open the file in an external application. 65 | For example, to open in Preview.app in macOS: 66 | ````lisp 67 | (setq arxiv-pdf-open-function (lambda (fpath) (call-process "open" nil 0 nil "-a" "/Applications/Preview.app" fpath))) 68 | ```` 69 | arxiv-mode will default to export the bibtex entry of paper to file `arxiv-default-bibliography`. 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /arxiv-query.el: -------------------------------------------------------------------------------- 1 | ;;; arxiv-query.el --- arXiv query functions -*- lexical-binding: t; -*- 2 | 3 | ;; This program is free software; you can redistribute it and/or 4 | ;; modify it under the terms of the GNU General Public License as 5 | ;; published by the Free Software Foundation; either version 2, or 6 | ;; (at your option) any later version. 7 | ;; 8 | ;; This program is distributed in the hope that it will be useful, 9 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | ;; General Public License for more details. 12 | ;; 13 | ;; You should have received a copy of the GNU General Public License 14 | ;; along with this program; see the file COPYING. If not, write to 15 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 16 | ;; Floor, Boston, MA 02110-1301, USA. 17 | 18 | ;;; Commentary: 19 | 20 | ;;; Code: 21 | 22 | (require 'xml) 23 | 24 | ;; URL of the arXiv api 25 | ;; check https://arxiv.org/help/api/user-manual for information 26 | (setq arxiv-url "http://export.arxiv.org/api/query") 27 | (setq arxiv-query-total-results nil) 28 | 29 | (defun arxiv-extract-pdf (my-list) 30 | "Extract the url for pdf file recursively from MY-LIST." 31 | (if my-list 32 | (let* ((sub-list (car (cdr (car my-list)))) 33 | (sub-title (cdr (assoc 'title sub-list)))) 34 | ;; (message "%S\n :%S" sub-list sub-title) 35 | (if (and sub-title (equal sub-title "pdf")) 36 | (progn 37 | ;; (message sub-title) 38 | (cdr (assoc 'href sub-list))) 39 | (arxiv-extract-pdf (cdr my-list)))))) 40 | 41 | (defun arxiv-parse-query-data (query-string) 42 | "Parse the input search data (QUERY-STRING) to pre-api form. 43 | In particular, replace space by + and \" to %22" 44 | (setq query-string (replace-regexp-in-string "\\(^ +\\| +$\\)" "" query-string)) 45 | (setq query-string (replace-regexp-in-string " +" "+" query-string)) 46 | (setq query-string (replace-regexp-in-string "\"" "%22" query-string)) 47 | query-string) 48 | 49 | (defun arxiv-get-api-url (&optional start) 50 | "Get the API url according to the `arxiv-query-data-list'. 51 | START specifies starting index (default 0). 52 | When using this function, make sure that the first item of the list has t condition." 53 | (unless start (setq start 0)) 54 | (let ((url (format "%s?search_query=" arxiv-url)) 55 | (body nil)) 56 | (dolist (query-data arxiv-query-data-list url) 57 | (if body 58 | (if (nth 1 query-data) 59 | (setq url (concat url "+AND+")) 60 | (setq url (concat url "+ANDNOT+"))) 61 | (setq body t)) 62 | (let ((field (car query-data))) 63 | (cond 64 | ((eq field 'all) (setq url (concat url "all:"))) 65 | ((eq field 'id) (setq url (concat url "id:"))) 66 | ((eq field 'time) (setq url (concat url "submittedDate:"))) 67 | ((eq field 'title) (setq url (concat url "ti:"))) 68 | ((eq field 'author) (setq url (concat url "au:"))) 69 | ((eq field 'abstract) (setq url (concat url "abs:"))) 70 | ((eq field 'comment) (setq url (concat url "co:"))) 71 | ((eq field 'journal) (setq url (concat url "jr:"))) 72 | ((eq field 'category) (setq url (concat url "cat:")))) 73 | (setq url (concat url (arxiv-parse-query-data (nth 2 query-data)))))) 74 | (if arxiv-query-sorting 75 | (setq url (concat url (format "&sortBy=%s&sortOrder=%s" 76 | (plist-get arxiv-query-sorting :sortby) 77 | (plist-get arxiv-query-sorting :sortorder))))) 78 | (setq url (concat url (format "&start=%d&max_results=%d" start arxiv-entries-per-fetch))))) 79 | 80 | (defun arxiv-geturl-date (date-start date-end category &optional start ascending) 81 | "Get the API url for articles between DATE-START and DATE-END in CATEGORY. 82 | START specifies starting index (default 0). 83 | If ASCENDING is t then sort the list by ascending order instead of descending." 84 | (unless start 85 | (setq start 0)) ; Start with the first result 86 | (if ascending 87 | (setq ascending "ascending") 88 | (setq ascending "descending")) 89 | (format "%s?search_query=submittedDate:[%s+TO+%s]+AND+cat:%s*&sortBy=submittedDate&sortOrder=%s&start=%d&max_results=%d" 90 | arxiv-url date-start date-end category ascending start arxiv-entries-per-fetch)) 91 | 92 | (defun arxiv-getxml-context (node child-name) 93 | "XML helper to get the context of CHILD-NAME from NODE directly." 94 | (car (xml-node-children (car (xml-get-children node child-name))))) 95 | 96 | (defun arxiv-parse-api (url) 97 | "Call arXiv api (at URL) and parse its response. 98 | Return a alist with various fields." 99 | (with-current-buffer (url-retrieve-synchronously url) 100 | (set-buffer-multibyte t) ;; enable utf-8 decoding 101 | ;; (view-buffer-other-window (current-buffer)) 102 | (let* ((root (car (xml-parse-region))) 103 | (entries (xml-get-children root 'entry)) 104 | (formated-list) (alist-entry) 105 | (pdf) (link) (id) (title) (abstract) (publishdate) (updatedate) (author) (names) (doi) (comment) (journal) (categories)) 106 | (condition-case exception 107 | (progn 108 | (setq arxiv-query-total-results (string-to-number (arxiv-getxml-context root 'opensearch:totalResults))) 109 | (setq arxiv-query-results-min (+ 1 (string-to-number (arxiv-getxml-context root 'opensearch:startIndex)))) 110 | (setq arxiv-query-results-max (+ arxiv-query-results-min -1 (string-to-number (arxiv-getxml-context root 'opensearch:itemsPerPage)))) 111 | (when (< arxiv-query-total-results arxiv-query-results-max) (setq arxiv-query-results-max arxiv-query-total-results)) 112 | (dolist (paper entries) 113 | (setq pdf (arxiv-extract-pdf (xml-get-children paper 'link))) 114 | (setq link (arxiv-getxml-context paper 'id)) 115 | (string-match "^http://arxiv\\.org/abs/\\(.+\\)$" link) 116 | (setq id (match-string 1 link)) 117 | (setq title (arxiv-getxml-context paper 'title)) 118 | (setq title (replace-regexp-in-string "[ \n]+" " " title)) 119 | (setq abstract (arxiv-getxml-context paper 'summary)) 120 | (setq abstract (replace-regexp-in-string "\n" " " abstract)) 121 | (setq publishdate (arxiv-getxml-context paper 'published)) 122 | (setq publishdate (replace-regexp-in-string "[TZ]" " " publishdate)) 123 | (setq updatedate (arxiv-getxml-context paper 'updated)) 124 | (setq updatedate (replace-regexp-in-string "[TZ]" " " updatedate)) 125 | (setq author (xml-get-children paper 'author)) 126 | (setq names (mapcar (lambda (author) (arxiv-getxml-context author 'name)) author)) 127 | (setq doi (arxiv-getxml-context paper 'arxiv:doi)) 128 | (setq comment (arxiv-getxml-context paper 'arxiv:comment)) 129 | (setq journal (arxiv-getxml-context paper 'arxiv:journal_ref)) 130 | (setq categories (xml-get-children paper 'category)) 131 | (setq categories (mapcar (lambda (cat) (cdr (assq 'term (xml-node-attributes cat)))) categories)) 132 | (setq alist-entry `((title . ,title) 133 | (author . ,names) 134 | (abstract . ,abstract) 135 | (url . ,link) 136 | (id . ,id) 137 | (date . ,publishdate) 138 | (updated . ,updatedate) 139 | (doi . ,doi) 140 | (comment . ,comment) 141 | (journal . ,journal) 142 | (categories . ,categories) 143 | (pdf . ,pdf))) 144 | (setq formated-list (append formated-list (list alist-entry)))) 145 | formated-list) 146 | ('error (progn 147 | (switch-to-buffer (get-buffer-create "*arxiv-debug*")) 148 | (print root (get-buffer "*arxiv-debug*")) 149 | (error "Cannot parse the API query result: %s. Refer to the debug buffer for information" exception))))))) 150 | 151 | (defun arxiv-query (cat date-start date-end &optional start ascending) 152 | "Query for articles in category CAT between DATE-START and DATE-END. 153 | START specifies starting index (default 0). 154 | If ASCENDING is t then sort the list by ascending order instead of descending." 155 | (unless (> (string-to-number date-end) (string-to-number date-start)) 156 | (user-error "Incorrect date specification")) 157 | (arxiv-parse-api (arxiv-geturl-date date-start date-end cat start ascending))) 158 | 159 | (defun arxiv-query-sort-cat (cat) 160 | "Sort the entry list according to the category CAT and cross-list." 161 | (when arxiv-entry-list 162 | (let ((main (seq-filter (lambda (x) (equal (car (alist-get 'categories x)) cat)) arxiv-entry-list)) 163 | (crosslist (seq-remove (lambda (x) (equal (car (alist-get 'categories x)) cat)) arxiv-entry-list))) 164 | (setq arxiv-entry-list (append main crosslist))))) 165 | 166 | (defun arxiv-query-general (&optional start) 167 | "Do a general query according to the list `arxiv-query-data-list'. 168 | START specifies starting index (default 0)." 169 | (arxiv-parse-api (arxiv-get-api-url start))) 170 | 171 | (provide 'arxiv-query) 172 | ;;; arxiv-query.el ends here 173 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | Emacs major mode for viewing updates on arXiv.org 294 | Copyright (C) 2013 Alex Chen 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /arxiv-vars.el: -------------------------------------------------------------------------------- 1 | ;;; arxiv-vars.el --- Defining the common variables for arxiv-mode -*- lexical-binding: t; -*- 2 | 3 | ;; This program is free software; you can redistribute it and/or 4 | ;; modify it under the terms of the GNU General Public License as 5 | ;; published by the Free Software Foundation; either version 2, or 6 | ;; (at your option) any later version. 7 | ;; 8 | ;; This program is distributed in the hope that it will be useful, 9 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | ;; General Public License for more details. 12 | ;; 13 | ;; You should have received a copy of the GNU General Public License 14 | ;; along with this program; see the file COPYING. If not, write to 15 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 16 | ;; Floor, Boston, MA 02110-1301, USA. 17 | 18 | ;;; Commentary: 19 | 20 | ;;; Code: 21 | 22 | (defgroup arxiv nil 23 | "A mode for reading arXiv abstracts." 24 | :prefix "arxiv-" 25 | :group 'applications) 26 | 27 | (defgroup arxiv-fontification nil 28 | "Faces for the arxiv mode." 29 | :group 'arxiv) 30 | 31 | (defgroup arxiv-preferences nil 32 | "General preferences for the arxiv mode." 33 | :group 'arxiv) 34 | 35 | (defvar arxiv-mode-hook nil 36 | "A list of functions to call when entering `arxiv-mode'.") 37 | 38 | (defvar arxiv-abstract-mode-hook nil 39 | "A list of functions to call when entering `arxiv-abstract-mode'.") 40 | 41 | (defvar arxiv-frame nil 42 | "Current frame accommodating `arxiv-mode'. 43 | Only used when `arxiv-pop-up-new-frame' is set to t.") 44 | 45 | (defvar arxiv-buffer nil 46 | "Current buffer for viewing arXiv updates.") 47 | 48 | (defvar arxiv-abstract-window nil 49 | "Current window for viewing the arXiv abstract.") 50 | 51 | (defvar arxiv-abstract-buffer nil 52 | "Current buffer for viewing the arXiv abstract.") 53 | 54 | (defvar arxiv-highlight-overlay nil 55 | "Overlay for displaying the selected article in arXiv article list.") 56 | 57 | (defvar arxiv-entry-list nil 58 | "Entries for arXiv articles.") 59 | 60 | (defvar arxiv-current-entry nil 61 | "Current entry in the arXiv article list.") 62 | 63 | (defvar arxiv-query-results-min nil 64 | "Current minimun entry of query result.") 65 | 66 | (defvar arxiv-query-results-max nil 67 | "Current maxmum entry of query result.") 68 | 69 | (defvar arxiv-query-data-list nil 70 | "List of current query data. 71 | Elements of this list must have the form (field condition context) 72 | Available fields are 'all, 'id, 'time, 'title, 'author, 'abstract, 73 | 'comment, 'journal and 'category. 74 | If condition is nil then the the search excludes the context and vice versa. 75 | context is a string seperated by quotes and spaces.") 76 | 77 | (defvar arxiv-query-sorting nil 78 | "A plist indicating how to sort arxiv query results. 79 | :sortby is one of `relevance', `lastUpdatedDate' or `submittedDate'; 80 | :sortorder either `ascending' or `descending'.") 81 | 82 | (defvar arxiv-order-info "Default" 83 | "The string giving the current sorting for arxiv query.") 84 | 85 | (defvar arxiv-query-info "" 86 | "A string containing the information of query data displayed in the header line.") 87 | 88 | (defvar arxiv-mode-entry-function nil 89 | "Variables showing the entry function used to enter `arxiv-mode'.") 90 | 91 | (defvar arxiv-categories 92 | '(CoRR cs.AI cs.AR cs.CC cs.CE cs.CG 93 | cs.CL cs.CR cs.CV cs.CY cs.DB cs.DC cs.DL cs.DM cs.DS cs.ET 94 | cs.FL cs.GL cs.GR cs.GT cs.HC cs.IR cs.IT cs.LG cs.LO cs.MA 95 | cs.MM cs.MS cs.NA cs.NE cs.NI cs.OH cs.OS cs.PF cs.PL cs.RO 96 | cs.SC cs.SD cs.SE cs.SI cs.SY econ econ.EM econ.GN econ.TH eess 97 | eess.AS eess.IV eess.SP eess.SY math math.AC math.AG math.AP 98 | math.AT math.CA math.CO math.CT math.CV math.DG math.DS math.FA 99 | math.GM math.GN math.GR math.GT math.HO math.IT math.KT math.LO 100 | math.MG math.MP math.NA math.NT math.OA math.OC math.PR math.QA 101 | math.RA math.RT math.SG math.SP math.ST astro-ph astro-ph.CO 102 | astro-ph.EP astro-ph.GA astro-ph.HE astro-ph.IM astro-ph.SR 103 | cond-mat cond-mat.dis-nn cond-mat.mes-hall cond-mat.mtrl-sci 104 | cond-mat.other cond-mat.quant-gas cond-mat.soft 105 | cond-mat.stat-mech cond-mat.str-el cond-mat.supr-con gr-qc 106 | hep-ex hep-lat hep-ph hep-th math-ph nlin nlin.AO nlin.CD 107 | nlin.CG nlin.PS nlin.SI nucl-ex nucl-th physics physics.acc-ph 108 | physics.ao-ph physics.app-ph physics.atm-clus physics.atom-ph 109 | physics.bio-ph physics.chem-ph physics.class-ph physics.comp-ph 110 | physics.data-an physics.ed-ph physics.flu-dyn physics.gen-ph 111 | physics.geo-ph physics.hist-ph physics.ins-det physics.med-ph 112 | physics.optics physics.plasm-ph physics.pop-ph physics.soc-ph 113 | physics.space-ph quant-ph q-bio q-bio.BM q-bio.CB q-bio.GN 114 | q-bio.MN q-bio.NC q-bio.OT q-bio.PE q-bio.QM q-bio.SC q-bio.TO 115 | q-fin q-fin.CP q-fin.EC q-fin.GN q-fin.MF q-fin.PM q-fin.PR 116 | q-fin.RM q-fin.ST q-fin.TR stat stat.AP stat.CO stat.ME stat.ML 117 | stat.OT stat.TH) 118 | "Availble categories in arXiv searching.") 119 | 120 | (defvar arxiv-subject-classifications 121 | '((cs.AI . "Artificial Intelligence") 122 | (cs.AR . "Hardware Architecture") 123 | (cs.CC . "Computational Complexity") 124 | (cs.CE . "Computational Engineering, Finance, and Science") 125 | (cs.CG . "Computational Geometry") 126 | (cs.CL . "Computation and Language") 127 | (cs.CR . "Cryptography and Security") 128 | (cs.CV . "Computer Vision and Pattern Recognition") 129 | (cs.CY . "Computers and Society") 130 | (cs.DB . "Databases") 131 | (cs.DC . "Distributed, Parallel, and Cluster Computing") 132 | (cs.DL . "Digital Libraries") 133 | (cs.DM . "Discrete Mathematics") 134 | (cs.DS . "Data Structures and Algorithms") 135 | (cs.ET . "Emerging Technologies") 136 | (cs.FL . "Formal Languages and Automata Theory") 137 | (cs.GL . "General Literature") 138 | (cs.GR . "Graphics") 139 | (cs.GT . "Computer Science and Game Theory") 140 | (cs.HC . "Human-Computer Interaction") 141 | (cs.IR . "Information Retrieval") 142 | (cs.IT . "Information Theory") 143 | (cs.LG . "Machine Learning") 144 | (cs.LO . "Logic in Computer Science") 145 | (cs.MA . "Multiagent Systems") 146 | (cs.MM . "Multimedia") 147 | (cs.MS . "Mathematical Software") 148 | (cs.NA . "Numerical Analysis") 149 | (cs.NE . "Neural and Evolutionary Computing") 150 | (cs.NI . "Networking and Internet Architecture") 151 | (cs.OH . "Other Computer Science") 152 | (cs.OS . "Operating Systems") 153 | (cs.PF . "Performance") 154 | (cs.PL . "Programming Languages") 155 | (cs.RO . "Robotics") 156 | (cs.SC . "Symbolic Computation") 157 | (cs.SD . "Sound") 158 | (cs.SE . "Software Engineering") 159 | (cs.SI . "Social and Information Networks") 160 | (cs.SY . "Systems and Control") 161 | (econ.EM . "Econometrics") 162 | (econ.GN . "General Economics") 163 | (econ.TH . "Theoretical Economics") 164 | (eess.AS . "Audio and Speech Processing") 165 | (eess.IV . "Image and Video Processing") 166 | (eess.SP . "Signal Processing") 167 | (eess.SY . "Systems and Control") 168 | (math.AC . "Commutative Algebra") 169 | (math.AG . "Algebraic Geometry") 170 | (math.AP . "Analysis of PDEs") 171 | (math.AT . "Algebraic Topology") 172 | (math.CA . "Classical Analysis and ODEs") 173 | (math.CO . "Combinatorics") 174 | (math.CT . "Category Theory") 175 | (math.CV . "Complex Variables") 176 | (math.DG . "Differential Geometry") 177 | (math.DS . "Dynamical Systems") 178 | (math.FA . "Functional Analysis") 179 | (math.GM . "General Mathematics") 180 | (math.GN . "General Topology") 181 | (math.GR . "Group Theory") 182 | (math.GT . "Geometric Topology") 183 | (math.HO . "History and Overview") 184 | (math.IT . "Information Theory") 185 | (math.KT . "K-Theory and Homology") 186 | (math.LO . "Logic") 187 | (math.MG . "Metric Geometry") 188 | (math.MP . "Mathematical Physics") 189 | (math.NA . "Numerical Analysis") 190 | (math.NT . "Number Theory") 191 | (math.OA . "Operator Algebras") 192 | (math.OC . "Optimization and Control") 193 | (math.PR . "Probability") 194 | (math.QA . "Quantum Algebra") 195 | (math.RA . "Rings and Algebras") 196 | (math.RT . "Representation Theory") 197 | (math.SG . "Symplectic Geometry") 198 | (math.SP . "Spectral Theory") 199 | (math.ST . "Statistics Theory") 200 | (astro-ph.CO . "Cosmology and Nongalactic Astrophysics") 201 | (astro-ph.EP . "Earth and Planetary Astrophysics") 202 | (astro-ph.GA . "Astrophysics of Galaxies") 203 | (astro-ph.HE . "High Energy Astrophysical Phenomena") 204 | (astro-ph.IM . "Instrumentation and Methods for Astrophysics") 205 | (astro-ph.SR . "Solar and Stellar Astrophysics") 206 | (cond-mat.dis-nn . "Disordered Systems and Neural Networks") 207 | (cond-mat.mes-hall . "Mesoscale and Nanoscale Physics") 208 | (cond-mat.mtrl-sci . "Materials Science") 209 | (cond-mat.other . "Other Condensed Matter") 210 | (cond-mat.quant-gas . "Quantum Gases") 211 | (cond-mat.soft . "Soft Condensed Matter") 212 | (cond-mat.stat-mech . "Statistical Mechanics") 213 | (cond-mat.str-el . "Strongly Correlated Electrons") 214 | (cond-mat.supr-con . "Superconductivity") 215 | (gr-qc . "General Relativity and Quantum Cosmology") 216 | (hep-ex . "High Energy Physics - Experiment") 217 | (hep-lat . "High Energy Physics - Lattice") 218 | (hep-ph . "High Energy Physics - Phenomenology") 219 | (hep-th . "High Energy Physics - Theory") 220 | (math-ph . "Mathematical Physics") 221 | (nlin.AO . "Adaptation and Self-Organizing Systems") 222 | (nlin.CD . "Chaotic Dynamics") 223 | (nlin.CG . "Cellular Automata and Lattice Gases") 224 | (nlin.PS . "Pattern Formation and Solitons") 225 | (nlin.SI . "Exactly Solvable and Integrable Systems") 226 | (nucl-ex . "Nuclear Experiment") 227 | (nucl-th . "Nuclear Theory") 228 | (physics.acc-ph . "Accelerator Physics") 229 | (physics.ao-ph . "Atmospheric and Oceanic Physics") 230 | (physics.app-ph . "Applied Physics") 231 | (physics.atm-clus . "Atomic and Molecular Clusters") 232 | (physics.atom-ph . "Atomic Physics") 233 | (physics.bio-ph . "Biological Physics") 234 | (physics.chem-ph . "Chemical Physics") 235 | (physics.class-ph . "Classical Physics") 236 | (physics.comp-ph . "Computational Physics") 237 | (physics.data-an . "Data Analysis, Statistics and Probability") 238 | (physics.ed-ph . "Physics Education") 239 | (physics.flu-dyn . "Fluid Dynamics") 240 | (physics.gen-ph . "General Physics") 241 | (physics.geo-ph . "Geophysics") 242 | (physics.hist-ph . "History and Philosophy of Physics") 243 | (physics.ins-det . "Instrumentation and Detectors") 244 | (physics.med-ph . "Medical Physics") 245 | (physics.optics . "Optics") 246 | (physics.plasm-ph . "Plasma Physics") 247 | (physics.pop-ph . "Popular Physics") 248 | (physics.soc-ph . "Physics and Society") 249 | (physics.space-ph . "Space Physics") 250 | (quant-ph . "Quantum Physics") 251 | (q-bio.BM . "Biomolecules") 252 | (q-bio.CB . "Cell Behavior") 253 | (q-bio.GN . "Genomics") 254 | (q-bio.MN . "Molecular Networks") 255 | (q-bio.NC . "Neurons and Cognition") 256 | (q-bio.OT . "Other Quantitative Biology") 257 | (q-bio.PE . "Populations and Evolution") 258 | (q-bio.QM . "Quantitative Methods") 259 | (q-bio.SC . "Subcellular Processes") 260 | (q-bio.TO . "Tissues and Organs") 261 | (q-fin.CP . "Computational Finance") 262 | (q-fin.EC . "Economics") 263 | (q-fin.GN . "General Finance") 264 | (q-fin.MF . "Mathematical Finance") 265 | (q-fin.PM . "Portfolio Management") 266 | (q-fin.PR . "Pricing of Securities") 267 | (q-fin.RM . "Risk Management") 268 | (q-fin.ST . "Statistical Finance") 269 | (q-fin.TR . "Trading and Market Microstructure") 270 | (stat.AP . "Applications") 271 | (stat.CO . "Computation") 272 | (stat.ME . "Methodology") 273 | (stat.ML . "Machine Learning") 274 | (stat.OT . "Other Statistics") 275 | (stat.TH . "Statistics Theory")) 276 | "ArXiv subjects alist for displaying.") 277 | 278 | (defcustom arxiv-pop-up-new-frame t 279 | "Whether to start `arxiv-mode' with a new pop-up frame." 280 | :group 'arxiv-preferences 281 | :type 'boolean) 282 | 283 | (defcustom arxiv-frame-alist '((name . "*arXiv*") (width . 240) (height . 80)) 284 | "The alist containing the property of arXiv pop-up frame." 285 | :group 'arxiv-preferences 286 | :type 'sexp) 287 | 288 | (defcustom arxiv-startup-with-abstract-window nil 289 | "Whether to start `arxiv-mode' with an abstract window." 290 | :group 'arxiv-preferences 291 | :type 'boolean) 292 | 293 | (defcustom arxiv-use-variable-pitch nil 294 | "Whether to use variable pitch fonts in `arxiv-mode' buffers." 295 | :group 'arxiv-preferences 296 | :type 'boolean) 297 | 298 | (defcustom arxiv-entries-per-fetch 100 299 | "Number of entries per page in the article list." 300 | :group 'arxiv-preferences 301 | :type 'integer) 302 | 303 | (defcustom arxiv-author-list-maximum 10 304 | "Maximum number of authors shown per entry on the article list. 305 | 0 means no maximum limit." 306 | :group 'arxiv-preferences 307 | :type 'integer) 308 | 309 | (defcustom arxiv-default-category "hep-th" 310 | "Default search category when using `arxiv-read'." 311 | :group 'arxiv-preferences 312 | :type 'string 313 | :options arxiv-categories) 314 | 315 | (defcustom arxiv-default-download-folder "~/Downloads/" 316 | "Default download folder to save PDF file." 317 | :group 'arxiv-preferences 318 | :type 'string) 319 | 320 | (defcustom arxiv-default-bibliography "" 321 | "Default master bibliography file to append for `arxiv-mode'." 322 | :group 'arxiv-preferences 323 | :type 'string) 324 | 325 | (defcustom arxiv-pdf-open-function 'find-file-other-window 326 | "Default function to open PDF file." 327 | :group 'arxiv-preferences 328 | :type 'function) 329 | 330 | ;; Defining custom faces 331 | (defvar arxiv-title-face 'arxiv-title-face) 332 | (defface arxiv-title-face 333 | '((t (:inherit font-lock-keyword-face :height 1.2))) 334 | "Face name for article titles in the arXiv article list." 335 | :group 'arxiv-fontification) 336 | 337 | (defvar arxiv-keyword-face 'arxiv-keyword-face) 338 | (defface arxiv-keyword-face 339 | '((t (:inherit font-lock-constant-face))) 340 | "Face name for keywords in the arXiv article list." 341 | :group 'arxiv-fontification) 342 | 343 | (defvar arxiv-author-face 'arxiv-author-face) 344 | (defface arxiv-author-face 345 | '((t (:inherit font-lock-type-face))) 346 | "Face name for authors in the arXiv article list." 347 | :group 'arxiv-fontification) 348 | 349 | (defvar arxiv-date-face 'arxiv-date-face) 350 | (defface arxiv-date-face 351 | '((t (:inherit shadow))) 352 | "Face name for date in the arXiv article list." 353 | :group 'arxiv-fontification) 354 | 355 | (defvar arxiv-abstract-face 'arxiv-abstract-face) 356 | (defface arxiv-abstract-face 357 | '((t (:inherit font-lock-doc-face))) 358 | "Face name for abstract in the arXiv abstract viewing window." 359 | :group 'arxiv-fontification) 360 | 361 | (defvar arxiv-abstract-title-face 'arxiv-abstract-title-face) 362 | (defface arxiv-abstract-title-face 363 | '((t (:inherit font-lock-keyword-face :height 1.5 :weight semi-bold :underline t))) 364 | "Face name for title in the arXiv abstract viewing window." 365 | :group 'arxiv-fontification) 366 | 367 | (defvar arxiv-abstract-author-face 'arxiv-abstract-author-face) 368 | (defface arxiv-abstract-author-face 369 | '((t (:inherit font-lock-type-face :height 1.2 :underline t))) 370 | "Face name for authors in the arXiv abstract viewing window." 371 | :group 'arxiv-fontification) 372 | 373 | (defvar arxiv-subfield-face 'arxiv-subfield-face) 374 | (defface arxiv-subfield-face 375 | '((t (:inherit default))) 376 | "Face name for subfields (comments, subjects, etc.) in the arXiv abstract viewing window." 377 | :group 'arxiv-fontification) 378 | 379 | (defvar arxiv-abstract-math-face 'arxiv-abstract-math-face) 380 | (defface arxiv-abstract-math-face 381 | '((t (:inherit font-lock-reference-face :family "Monospace"))) 382 | "Face name for the latex content in abstract in the arXiv abstract viewing window." 383 | :group 'arxiv-fontification) 384 | 385 | (defvar arxiv-abstract-syntax-table 386 | (let ((synTable (make-syntax-table text-mode-syntax-table))) 387 | (modify-syntax-entry ?$ "($" synTable) 388 | (modify-syntax-entry ?$ ")$" synTable) 389 | synTable)) 390 | 391 | (defvar arxiv-abstract-prettify-symbols-alist 392 | '( ;; Lowercase Greek letters. 393 | ("\\alpha" . ?α) 394 | ("\\beta" . ?β) 395 | ("\\gamma" . ?γ) 396 | ("\\delta" . ?δ) 397 | ("\\epsilon" . ?ϵ) 398 | ("\\zeta" . ?ζ) 399 | ("\\eta" . ?η) 400 | ("\\theta" . ?θ) 401 | ("\\iota" . ?ι) 402 | ("\\kappa" . ?κ) 403 | ("\\lambda" . ?λ) 404 | ("\\mu" . ?μ) 405 | ("\\nu" . ?ν) 406 | ("\\xi" . ?ξ) 407 | ;; There is no \omicron because it looks like a latin o. 408 | ("\\pi" . ?π) 409 | ("\\rho" . ?ρ) 410 | ("\\sigma" . ?σ) 411 | ("\\tau" . ?τ) 412 | ("\\upsilon" . ?υ) 413 | ("\\phi" . ?φ) 414 | ("\\chi" . ?χ) 415 | ("\\psi" . ?ψ) 416 | ("\\omega" . ?ω) 417 | ;; Uppercase Greek letters. 418 | ("\\Gamma" . ?Γ) 419 | ("\\Delta" . ?Δ) 420 | ("\\Lambda" . ?Λ) 421 | ("\\Phi" . ?Φ) 422 | ("\\Pi" . ?Π) 423 | ("\\Psi" . ?Ψ) 424 | ("\\Sigma" . ?Σ) 425 | ("\\Theta" . ?Θ) 426 | ("\\Upsilon" . ?Υ) 427 | ("\\Xi" . ?Ξ) 428 | ("\\Omega" . ?Ω) 429 | 430 | ;; Other math symbols (taken from leim/quail/latin-ltx.el). 431 | ("\\Box" . ?□) 432 | ("\\Bumpeq" . ?≎) 433 | ("\\Cap" . ?⋒) 434 | ("\\Cup" . ?⋓) 435 | ("\\Diamond" . ?◇) 436 | ("\\Downarrow" . ?⇓) 437 | ("\\H{o}" . ?ő) 438 | ("\\Im" . ?ℑ) 439 | ("\\Join" . ?⋈) 440 | ("\\Leftarrow" . ?⇐) 441 | ("\\Leftrightarrow" . ?⇔) 442 | ("\\Ll" . ?⋘) 443 | ("\\Lleftarrow" . ?⇚) 444 | ("\\Longleftarrow" . ?⇐) 445 | ("\\Longleftrightarrow" . ?⇔) 446 | ("\\Longrightarrow" . ?⇒) 447 | ("\\Lsh" . ?↰) 448 | ("\\Re" . ?ℜ) 449 | ("\\Rightarrow" . ?⇒) 450 | ("\\Rrightarrow" . ?⇛) 451 | ("\\Rsh" . ?↱) 452 | ("\\Subset" . ?⋐) 453 | ("\\Supset" . ?⋑) 454 | ("\\Uparrow" . ?⇑) 455 | ("\\Updownarrow" . ?⇕) 456 | ("\\Vdash" . ?⊩) 457 | ("\\Vert" . ?‖) 458 | ("\\Vvdash" . ?⊪) 459 | ("\\aleph" . ?ℵ) 460 | ("\\amalg" . ?∐) 461 | ("\\angle" . ?∠) 462 | ("\\approx" . ?≈) 463 | ("\\approxeq" . ?≊) 464 | ("\\ast" . ?∗) 465 | ("\\asymp" . ?≍) 466 | ("\\backcong" . ?≌) 467 | ("\\backepsilon" . ?∍) 468 | ("\\backprime" . ?‵) 469 | ("\\backsim" . ?∽) 470 | ("\\backsimeq" . ?⋍) 471 | ("\\backslash" . ?\\) 472 | ("\\barwedge" . ?⊼) 473 | ("\\because" . ?∵) 474 | ("\\beth" . ?ℶ) 475 | ("\\between" . ?≬) 476 | ("\\bigcap" . ?⋂) 477 | ("\\bigcirc" . ?◯) 478 | ("\\bigcup" . ?⋃) 479 | ("\\bigstar" . ?★) 480 | ("\\bigtriangledown" . ?▽) 481 | ("\\bigtriangleup" . ?△) 482 | ("\\bigvee" . ?⋁) 483 | ("\\bigwedge" . ?⋀) 484 | ("\\blacklozenge" . ?✦) 485 | ("\\blacksquare" . ?▪) 486 | ("\\blacktriangle" . ?▴) 487 | ("\\blacktriangledown" . ?▾) 488 | ("\\blacktriangleleft" . ?◂) 489 | ("\\blacktriangleright" . ?▸) 490 | ("\\bot" . ?⊥) 491 | ("\\bowtie" . ?⋈) 492 | ("\\boxminus" . ?⊟) 493 | ("\\boxplus" . ?⊞) 494 | ("\\boxtimes" . ?⊠) 495 | ("\\bullet" . ?•) 496 | ("\\bumpeq" . ?≏) 497 | ("\\cap" . ?∩) 498 | ("\\cdots" . ?⋯) 499 | ("\\centerdot" . ?·) 500 | ("\\checkmark" . ?✓) 501 | ("\\chi" . ?χ) 502 | ("\\cdot" . ?⋅) 503 | ("\\cdots" . ?⋯) 504 | ("\\circ" . ?∘) 505 | ("\\circeq" . ?≗) 506 | ("\\circlearrowleft" . ?↺) 507 | ("\\circlearrowright" . ?↻) 508 | ("\\circledR" . ?®) 509 | ("\\circledS" . ?Ⓢ) 510 | ("\\circledast" . ?⊛) 511 | ("\\circledcirc" . ?⊚) 512 | ("\\circleddash" . ?⊝) 513 | ("\\clubsuit" . ?♣) 514 | ("\\coloneq" . ?≔) 515 | ("\\complement" . ?∁) 516 | ("\\cong" . ?≅) 517 | ("\\coprod" . ?∐) 518 | ("\\cup" . ?∪) 519 | ("\\curlyeqprec" . ?⋞) 520 | ("\\curlyeqsucc" . ?⋟) 521 | ("\\curlypreceq" . ?≼) 522 | ("\\curlyvee" . ?⋎) 523 | ("\\curlywedge" . ?⋏) 524 | ("\\curvearrowleft" . ?↶) 525 | ("\\curvearrowright" . ?↷) 526 | ("\\dag" . ?†) 527 | ("\\dagger" . ?†) 528 | ("\\daleth" . ?ℸ) 529 | ("\\dashv" . ?⊣) 530 | ("\\ddag" . ?‡) 531 | ("\\ddagger" . ?‡) 532 | ("\\ddots" . ?⋱) 533 | ("\\diamond" . ?⋄) 534 | ("\\diamondsuit" . ?♢) 535 | ("\\divideontimes" . ?⋇) 536 | ("\\doteq" . ?≐) 537 | ("\\doteqdot" . ?≑) 538 | ("\\dotplus" . ?∔) 539 | ("\\dotsquare" . ?⊡) 540 | ("\\downarrow" . ?↓) 541 | ("\\downdownarrows" . ?⇊) 542 | ("\\downleftharpoon" . ?⇃) 543 | ("\\downrightharpoon" . ?⇂) 544 | ("\\ell" . ?ℓ) 545 | ("\\emptyset" . ?∅) 546 | ("\\eqcirc" . ?≖) 547 | ("\\eqcolon" . ?≕) 548 | ("\\eqslantgtr" . ?⋝) 549 | ("\\eqslantless" . ?⋜) 550 | ("\\equiv" . ?≡) 551 | ("\\exists" . ?∃) 552 | ("\\fallingdotseq" . ?≒) 553 | ("\\flat" . ?♭) 554 | ("\\forall" . ?∀) 555 | ("\\frown" . ?⌢) 556 | ("\\ge" . ?≥) 557 | ("\\geq" . ?≥) 558 | ("\\geqq" . ?≧) 559 | ("\\geqslant" . ?≥) 560 | ("\\gets" . ?←) 561 | ("\\gg" . ?≫) 562 | ("\\ggg" . ?⋙) 563 | ("\\gimel" . ?ℷ) 564 | ("\\gnapprox" . ?⋧) 565 | ("\\gneq" . ?≩) 566 | ("\\gneqq" . ?≩) 567 | ("\\gnsim" . ?⋧) 568 | ("\\gtrapprox" . ?≳) 569 | ("\\gtrdot" . ?⋗) 570 | ("\\gtreqless" . ?⋛) 571 | ("\\gtreqqless" . ?⋛) 572 | ("\\gtrless" . ?≷) 573 | ("\\gtrsim" . ?≳) 574 | ("\\gvertneqq" . ?≩) 575 | ("\\hbar" . ?ℏ) 576 | ("\\heartsuit" . ?♥) 577 | ("\\hookleftarrow" . ?↩) 578 | ("\\hookrightarrow" . ?↪) 579 | ("\\iff" . ?⇔) 580 | ("\\imath" . ?ı) 581 | ("\\in" . ?∈) 582 | ("\\infty" . ?∞) 583 | ("\\int" . ?∫) 584 | ("\\intercal" . ?⊺) 585 | ("\\langle" . 10216) ; Literal ?⟨ breaks indentation. 586 | ("\\lbrace" . ?{) 587 | ("\\lbrack" . ?\[) 588 | ("\\lceil" . ?⌈) 589 | ("\\ldots" . ?…) 590 | ("\\le" . ?≤) 591 | ("\\leadsto" . ?↝) 592 | ("\\leftarrow" . ?←) 593 | ("\\leftarrowtail" . ?↢) 594 | ("\\leftharpoondown" . ?↽) 595 | ("\\leftharpoonup" . ?↼) 596 | ("\\leftleftarrows" . ?⇇) 597 | ;; ("\\leftparengtr" ?〈), see bug#12948. 598 | ("\\leftrightarrow" . ?↔) 599 | ("\\leftrightarrows" . ?⇆) 600 | ("\\leftrightharpoons" . ?⇋) 601 | ("\\leftrightsquigarrow" . ?↭) 602 | ("\\leftthreetimes" . ?⋋) 603 | ("\\leq" . ?≤) 604 | ("\\leqq" . ?≦) 605 | ("\\leqslant" . ?≤) 606 | ("\\lessapprox" . ?≲) 607 | ("\\lessdot" . ?⋖) 608 | ("\\lesseqgtr" . ?⋚) 609 | ("\\lesseqqgtr" . ?⋚) 610 | ("\\lessgtr" . ?≶) 611 | ("\\lesssim" . ?≲) 612 | ("\\lfloor" . ?⌊) 613 | ("\\lhd" . ?◁) 614 | ("\\rhd" . ?▷) 615 | ("\\ll" . ?≪) 616 | ("\\llcorner" . ?⌞) 617 | ("\\lnapprox" . ?⋦) 618 | ("\\lneq" . ?≨) 619 | ("\\lneqq" . ?≨) 620 | ("\\lnsim" . ?⋦) 621 | ("\\longleftarrow" . ?←) 622 | ("\\longleftrightarrow" . ?↔) 623 | ("\\longmapsto" . ?↦) 624 | ("\\longrightarrow" . ?→) 625 | ("\\looparrowleft" . ?↫) 626 | ("\\looparrowright" . ?↬) 627 | ("\\lozenge" . ?✧) 628 | ("\\lq" . ?‘) 629 | ("\\lrcorner" . ?⌟) 630 | ("\\ltimes" . ?⋉) 631 | ("\\lvertneqq" . ?≨) 632 | ("\\maltese" . ?✠) 633 | ("\\mapsto" . ?↦) 634 | ("\\measuredangle" . ?∡) 635 | ("\\mho" . ?℧) 636 | ("\\mid" . ?∣) 637 | ("\\models" . ?⊧) 638 | ("\\mp" . ?∓) 639 | ("\\multimap" . ?⊸) 640 | ("\\nLeftarrow" . ?⇍) 641 | ("\\nLeftrightarrow" . ?⇎) 642 | ("\\nRightarrow" . ?⇏) 643 | ("\\nVDash" . ?⊯) 644 | ("\\nVdash" . ?⊮) 645 | ("\\nabla" . ?∇) 646 | ("\\napprox" . ?≉) 647 | ("\\natural" . ?♮) 648 | ("\\ncong" . ?≇) 649 | ("\\ne" . ?≠) 650 | ("\\nearrow" . ?↗) 651 | ("\\neg" . ?¬) 652 | ("\\neq" . ?≠) 653 | ("\\nequiv" . ?≢) 654 | ("\\newline" . ?
) 655 | ("\\nexists" . ?∄) 656 | ("\\ngeq" . ?≱) 657 | ("\\ngeqq" . ?≱) 658 | ("\\ngeqslant" . ?≱) 659 | ("\\ngtr" . ?≯) 660 | ("\\ni" . ?∋) 661 | ("\\nleftarrow" . ?↚) 662 | ("\\nleftrightarrow" . ?↮) 663 | ("\\nleq" . ?≰) 664 | ("\\nleqq" . ?≰) 665 | ("\\nleqslant" . ?≰) 666 | ("\\nless" . ?≮) 667 | ("\\nmid" . ?∤) 668 | ;; ("\\not" ?̸) ;FIXME: conflict with "NOT SIGN" ¬. 669 | ("\\notin" . ?∉) 670 | ("\\nparallel" . ?∦) 671 | ("\\nprec" . ?⊀) 672 | ("\\npreceq" . ?⋠) 673 | ("\\nrightarrow" . ?↛) 674 | ("\\nshortmid" . ?∤) 675 | ("\\nshortparallel" . ?∦) 676 | ("\\nsim" . ?≁) 677 | ("\\nsimeq" . ?≄) 678 | ("\\nsubset" . ?⊄) 679 | ("\\nsubseteq" . ?⊈) 680 | ("\\nsubseteqq" . ?⊈) 681 | ("\\nsucc" . ?⊁) 682 | ("\\nsucceq" . ?⋡) 683 | ("\\nsupset" . ?⊅) 684 | ("\\nsupseteq" . ?⊉) 685 | ("\\nsupseteqq" . ?⊉) 686 | ("\\ntriangleleft" . ?⋪) 687 | ("\\ntrianglelefteq" . ?⋬) 688 | ("\\ntriangleright" . ?⋫) 689 | ("\\ntrianglerighteq" . ?⋭) 690 | ("\\nvDash" . ?⊭) 691 | ("\\nvdash" . ?⊬) 692 | ("\\nwarrow" . ?↖) 693 | ("\\odot" . ?⊙) 694 | ("\\oint" . ?∮) 695 | ("\\ominus" . ?⊖) 696 | ("\\oplus" . ?⊕) 697 | ("\\oslash" . ?⊘) 698 | ("\\otimes" . ?⊗) 699 | ("\\par" . ?
) 700 | ("\\parallel" . ?∥) 701 | ("\\partial" . ?∂) 702 | ("\\perp" . ?⊥) 703 | ("\\pitchfork" . ?⋔) 704 | ("\\prec" . ?≺) 705 | ("\\precapprox" . ?≾) 706 | ("\\preceq" . ?≼) 707 | ("\\precnapprox" . ?⋨) 708 | ("\\precnsim" . ?⋨) 709 | ("\\precsim" . ?≾) 710 | ("\\prime" . ?′) 711 | ("\\prod" . ?∏) 712 | ("\\propto" . ?∝) 713 | ("\\qed" . ?∎) 714 | ("\\qquad" . ?⧢) 715 | ("\\quad" . ?␣) 716 | ("\\rangle" . 10217) ; Literal ?⟩ breaks indentation. 717 | ("\\rbrace" . ?}) 718 | ("\\rbrack" . ?\]) 719 | ("\\rceil" . ?⌉) 720 | ("\\rfloor" . ?⌋) 721 | ("\\rightarrow" . ?→) 722 | ("\\rightarrowtail" . ?↣) 723 | ("\\rightharpoondown" . ?⇁) 724 | ("\\rightharpoonup" . ?⇀) 725 | ("\\rightleftarrows" . ?⇄) 726 | ("\\rightleftharpoons" . ?⇌) 727 | ;; ("\\rightparengtr" ?⦔) ;; Was ?〉, see bug#12948. 728 | ("\\rightrightarrows" . ?⇉) 729 | ("\\rightthreetimes" . ?⋌) 730 | ("\\risingdotseq" . ?≓) 731 | ("\\rtimes" . ?⋊) 732 | ("\\times" . ?×) 733 | ("\\sbs" . ?﹨) 734 | ("\\searrow" . ?↘) 735 | ("\\setminus" . ?∖) 736 | ("\\sharp" . ?♯) 737 | ("\\shortmid" . ?∣) 738 | ("\\shortparallel" . ?∥) 739 | ("\\sim" . ?∼) 740 | ("\\simeq" . ?≃) 741 | ("\\smallamalg" . ?∐) 742 | ("\\smallsetminus" . ?∖) 743 | ("\\smallsmile" . ?⌣) 744 | ("\\smile" . ?⌣) 745 | ("\\spadesuit" . ?♠) 746 | ("\\sphericalangle" . ?∢) 747 | ("\\sqcap" . ?⊓) 748 | ("\\sqcup" . ?⊔) 749 | ("\\sqsubset" . ?⊏) 750 | ("\\sqsubseteq" . ?⊑) 751 | ("\\sqsupset" . ?⊐) 752 | ("\\sqsupseteq" . ?⊒) 753 | ("\\square" . ?□) 754 | ("\\squigarrowright" . ?⇝) 755 | ("\\star" . ?⋆) 756 | ("\\straightphi" . ?φ) 757 | ("\\subset" . ?⊂) 758 | ("\\subseteq" . ?⊆) 759 | ("\\subseteqq" . ?⊆) 760 | ("\\subsetneq" . ?⊊) 761 | ("\\subsetneqq" . ?⊊) 762 | ("\\succ" . ?≻) 763 | ("\\succapprox" . ?≿) 764 | ("\\succcurlyeq" . ?≽) 765 | ("\\succeq" . ?≽) 766 | ("\\succnapprox" . ?⋩) 767 | ("\\succnsim" . ?⋩) 768 | ("\\succsim" . ?≿) 769 | ("\\sum" . ?∑) 770 | ("\\supset" . ?⊃) 771 | ("\\supseteq" . ?⊇) 772 | ("\\supseteqq" . ?⊇) 773 | ("\\supsetneq" . ?⊋) 774 | ("\\supsetneqq" . ?⊋) 775 | ("\\surd" . ?√) 776 | ("\\swarrow" . ?↙) 777 | ("\\therefore" . ?∴) 778 | ("\\thickapprox" . ?≈) 779 | ("\\thicksim" . ?∼) 780 | ("\\to" . ?→) 781 | ("\\top" . ?⊤) 782 | ("\\triangle" . ?▵) 783 | ("\\triangledown" . ?▿) 784 | ("\\triangleleft" . ?◃) 785 | ("\\trianglelefteq" . ?⊴) 786 | ("\\triangleq" . ?≜) 787 | ("\\triangleright" . ?▹) 788 | ("\\trianglerighteq" . ?⊵) 789 | ("\\twoheadleftarrow" . ?↞) 790 | ("\\twoheadrightarrow" . ?↠) 791 | ("\\ulcorner" . ?⌜) 792 | ("\\uparrow" . ?↑) 793 | ("\\updownarrow" . ?↕) 794 | ("\\upleftharpoon" . ?↿) 795 | ("\\uplus" . ?⊎) 796 | ("\\uprightharpoon" . ?↾) 797 | ("\\upuparrows" . ?⇈) 798 | ("\\urcorner" . ?⌝) 799 | ("\\u{i}" . ?ĭ) 800 | ("\\vDash" . ?⊨) 801 | ("\\varepsilon" . ?ε) 802 | ("\\varprime" . ?′) 803 | ("\\varpropto" . ?∝) 804 | ("\\varrho" . ?ϱ) 805 | ;; ("\\varsigma" ?ς) ;FIXME: Looks reversed with the non\var. 806 | ("\\vartriangleleft" . ?⊲) 807 | ("\\vartriangleright" . ?⊳) 808 | ("\\vdash" . ?⊢) 809 | ("\\vdots" . ?⋮) 810 | ("\\vee" . ?∨) 811 | ("\\veebar" . ?⊻) 812 | ("\\vert" . ?|) 813 | ("\\wedge" . ?∧) 814 | ("\\wp" . ?℘) 815 | ("\\wr" . ?≀) 816 | ("\\Bbb{N}" . ?ℕ) ; AMS commands for blackboard bold 817 | ("\\Bbb{P}" . ?ℙ) ; Also sometimes \mathbb. 818 | ("\\Bbb{Q}" . ?ℚ) 819 | ("\\Bbb{R}" . ?ℝ) 820 | ("\\Bbb{Z}" . ?ℤ) 821 | ("--" . ?–) 822 | ("---" . ?—) 823 | ("\\ordfeminine" . ?ª) 824 | ("\\ordmasculine" . ?º) 825 | ("\\lambdabar" . ?ƛ) 826 | ("\\celsius" . ?℃) 827 | ("\\textmu" . ?µ) 828 | ("\\textfractionsolidus" . ?⁄) 829 | ("\\textbigcircle" . ?⃝) 830 | ("\\textmusicalnote" . ?♪) 831 | ("\\textdied" . ?✝) 832 | ("\\textcolonmonetary" . ?₡) 833 | ("\\textwon" . ?₩) 834 | ("\\textnaira" . ?₦) 835 | ("\\textpeso" . ?₱) 836 | ("\\textlira" . ?₤) 837 | ("\\textrecipe" . ?℞) 838 | ("\\textinterrobang" . ?‽) 839 | ("\\textpertenthousand" . ?‱) 840 | ("\\textbaht" . ?฿) 841 | ("\\textnumero" . ?№) 842 | ("\\textdiscount" . ?⁒) 843 | ("\\textestimated" . ?℮) 844 | ("\\textopenbullet" . ?◦) 845 | ("\\textlquill" . 8261) ; Literal ?⁅ breaks indentation. 846 | ("\\textrquill" . 8262) ; Literal ?⁆ breaks indentation. 847 | ("\\textcircledP" . ?℗) 848 | ("\\textreferencemark" . ?※)) 849 | "A `prettify-symbols-alist' used when viewing math source code. 850 | Taken from for tex-mode.el.") 851 | 852 | 853 | (provide 'arxiv-vars) 854 | 855 | ;;; arxiv-vars.el ends here 856 | -------------------------------------------------------------------------------- /arxiv-mode.el: -------------------------------------------------------------------------------- 1 | ;;; arxiv-mode.el --- Read and search for articles on arXiv.org -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2013-2023 Alex Chen, Simon Lin 4 | 5 | ;; Author: Alex Chen (fizban007) 6 | ;; Simon Lin (Simon-Lin) 7 | ;; URL: https://github.com/fizban007/arxiv-mode 8 | ;; Version: 0.3.2 9 | ;; Keywords: bib, convenience, hypermedia 10 | ;; Package-Requires: ((emacs "27.1") (hydra "0")) 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;; This program is free software; you can redistribute it and/or 14 | ;; modify it under the terms of the GNU General Public License as 15 | ;; published by the Free Software Foundation; either version 2, or (at 16 | ;; your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, but 19 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | ;; General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program ; see the file COPYING. If not, write to 25 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 26 | ;; Boston, MA 02111-1307, USA. 27 | 28 | ;;; Commentary: 29 | ;; 30 | ;; arxiv-mode is an Emacs major mode for viewing 31 | ;; updates on arXiv.org. 32 | ;; 33 | ;; 34 | ;; Common Usage 35 | ;; ============ 36 | ;; 37 | ;; arxiv-mode provides many functions for accessing arXiv.org. 38 | ;; To browse the daily new submissions list in a category, run `M-x arxiv-read-new`. 39 | ;; To browse the recent (weekly) submissions, run `M-x arxiv-read-recent`. 40 | ;; Use `M-x arxiv-read-author` to search for specific author(s). 41 | ;; Use `M-x arxiv-search` to perform a simple search on the arXiv database. 42 | ;; 43 | ;; For more complicated searches, use `M-x arxiv-complex-search`. 44 | ;; This command allows user to dynamically refine and modify search conditions. 45 | ;; You can also use `r` to refine search condition in the abstract list obtained from a search. 46 | ;; 47 | ;; In the article list, use `n` and `p` to navigate the article list. 48 | ;; Press `SPC` to toggle visibility of the abstract window. 49 | ;; Press `RET` to open the entry in a web browser. Press `d` to download the pdf. 50 | ;; Press `b` to export the bibtex entry of current paper to your specified .bib file. 51 | ;; Press `B` to export the bibtex entry to a new buffer. 52 | ;; Press `e` to download pdf and add a bibtex entry with a link to the actual pdf file. 53 | ;; 54 | ;; All available commands are listed in a hydra help menu accessable by `?` whenever you are in the article list. 55 | ;; 56 | ;; 57 | ;; Installation 58 | ;; ============ 59 | ;; 60 | ;; arxiv-mode is available on MELPA. 61 | ;; After `M-x package-install RET arxiv-mode RET`, put the following code in your `init.el`: 62 | ;; 63 | ;; (require 'arxiv-mode) 64 | ;; 65 | ;; Or if you use `use-package`, you can simply put: 66 | ;; (use-package arxiv-mode 67 | ;; :ensure t) 68 | ;; 69 | ;; into the your init file. `use-package` will automatically download `arxiv-mode` for you. 70 | ;; 71 | ;; 72 | ;; Customization 73 | ;; ============= 74 | ;; 75 | ;; Run `M-x arxiv-customize` to customize or set the customization variables directly. 76 | 77 | 78 | 79 | 80 | ;;; Code: 81 | 82 | (require 'seq) 83 | (require 'button) 84 | (require 'hydra) 85 | (require 'bibtex) 86 | (require 'arxiv-vars) 87 | (require 'arxiv-query) 88 | 89 | (defvar arxiv-mode-map 90 | (let ((map (make-sparse-keymap))) 91 | (define-key map (kbd "p") 'arxiv-prev-entry) 92 | (define-key map (kbd "n") 'arxiv-next-entry) 93 | (define-key map (kbd "RET") 'arxiv-open-current-url) 94 | (define-key map (kbd "SPC") 'arxiv-SPC) 95 | (define-key map (kbd "d") 'arxiv-download-pdf) 96 | (define-key map (kbd "e") 'arxiv-download-pdf-export-bibtex) 97 | (define-key map (kbd "b") 'arxiv-export-bibtex) 98 | (define-key map (kbd "B") 'arxiv-export-bibtex-to-buffer) 99 | (define-key map (kbd "r") 'arxiv-refine-search) 100 | (define-key map (kbd "q") 'arxiv-exit) 101 | (define-key map (kbd "?") 'arxiv-help-menu/body) 102 | (define-key map (kbd "") 'arxiv-click-select-entry) 103 | map)) 104 | 105 | (define-derived-mode arxiv-mode special-mode "arXiv" 106 | "Major mode for reading updates and searching on arXiv.org. 107 | Type SPC to expand details on selected entry. 108 | Type RET to visit the corresponding entry on arXiv.org in browser. 109 | Type ? to invoke major commands." 110 | :group 'arxiv 111 | (setq header-line-format '(:eval (arxiv-headerline-format))) 112 | (setq arxiv-highlight-overlay (make-overlay 1 1)) 113 | (overlay-put arxiv-highlight-overlay 'face '(:inherit highlight :extend t)) 114 | (if arxiv-use-variable-pitch 115 | (variable-pitch-mode 1) 116 | (variable-pitch-mode -1))) 117 | 118 | (defvar arxiv-abstract-mode-map 119 | (let ((map (make-sparse-keymap))) 120 | (define-key map (kbd "RET") 'arxiv-open-current-url) 121 | (define-key map (kbd "SPC") 'arxiv-toggle-abstract) 122 | (define-key map (kbd "d") 'arxiv-download-pdf) 123 | (define-key map (kbd "e") 'arxiv-download-pdf-export-bibtex) 124 | (define-key map (kbd "b") 'arxiv-export-bibtex) 125 | (define-key map (kbd "B") 'arxiv-export-bibtex-to-buffer) 126 | (define-key map (kbd "q") 'arxiv-exit) 127 | map)) 128 | 129 | (define-derived-mode arxiv-abstract-mode special-mode "arXiv-abstract" 130 | "Major mode for reading arXiv abstracts." 131 | (if arxiv-use-variable-pitch 132 | (variable-pitch-mode 1) 133 | (variable-pitch-mode -1))) 134 | 135 | (defun arxiv-insert-with-face (string face-property) 136 | "Wrapper function to insert STRING with given FACE-PROPERTY." 137 | (insert (propertize string 'font-lock-face face-property))) 138 | 139 | (defun arxiv-next-entry (&optional arg) 140 | "Move to the next arXiv entry. 141 | With ARG, move to the next nth entry." 142 | (interactive "P") 143 | (setq arxiv-current-entry (+ arxiv-current-entry (prefix-numeric-value arg))) 144 | (let ((len (- (safe-length arxiv-entry-list) 1))) 145 | (when (>= arxiv-current-entry len) 146 | (if (eq arxiv-query-results-max arxiv-query-total-results) 147 | (when (> arxiv-current-entry len) 148 | (setq arxiv-current-entry (- (safe-length arxiv-entry-list) 1)) 149 | (message "end of search results")) 150 | (arxiv-show-next-page)))) 151 | (goto-char (point-min)) 152 | (forward-line (* 4 arxiv-current-entry)) 153 | (move-overlay arxiv-highlight-overlay 154 | (point) (progn (beginning-of-line 5) (point))) 155 | (forward-line (- 4)) 156 | (when arxiv-abstract-window 157 | (arxiv-show-abstract))) 158 | 159 | (defun arxiv-prev-entry (&optional arg) 160 | "Move to the previous arXiv entry. 161 | With ARG, move to the previous nth entry." 162 | (interactive "P") 163 | (setq arxiv-current-entry (- arxiv-current-entry (prefix-numeric-value arg))) 164 | (when (< arxiv-current-entry 0) 165 | (setq arxiv-current-entry 0) 166 | (message "beginning of search results")) 167 | (goto-char (point-min)) 168 | (forward-line (* 4 arxiv-current-entry)) 169 | (move-overlay arxiv-highlight-overlay 170 | (point) (progn (beginning-of-line 5) (point))) 171 | (forward-line (- 4)) 172 | (when (window-live-p arxiv-abstract-window) 173 | (arxiv-show-abstract))) 174 | 175 | (defun arxiv-select-entry () 176 | "Select the entry to which the cursor is pointing to." 177 | (interactive) 178 | (setq arxiv-current-entry (/ (line-number-at-pos) 4)) 179 | (goto-char (point-min)) 180 | (forward-line (* 4 arxiv-current-entry)) 181 | (move-overlay arxiv-highlight-overlay 182 | (point) (progn (beginning-of-line 5) (point))) 183 | (forward-line (- 4)) 184 | (when (window-live-p arxiv-abstract-window) 185 | (arxiv-show-abstract))) 186 | 187 | (defun arxiv-click-select-entry (ev) 188 | "Select the entry at which the mouse is currently pointing. 189 | This should be bound to a mouse click event EV." 190 | (interactive "e") 191 | (mouse-set-point ev) 192 | (arxiv-select-entry) 193 | (arxiv-show-abstract)) 194 | 195 | (defun arxiv-open-current-url () 196 | "Open the webpage for the current highlighted paper entry." 197 | (interactive) 198 | (browse-url (cdr (assoc 'url (nth arxiv-current-entry arxiv-entry-list))))) 199 | 200 | (defun arxiv-download-pdf (&optional confirm) 201 | "Download and save the highlighted paper to desired folder. 202 | Return the path of the saved pdf file. You can change the 203 | default folder by customizing the variable 204 | `arxiv-default-download-folder'. If CONFIRM is t, don't prompt the 205 | user with opening file." 206 | (interactive) 207 | (let ((url (cdr (assoc 'pdf (nth arxiv-current-entry arxiv-entry-list)))) 208 | (newfile) (pdfname) (input)) 209 | (string-match "/\\([^/]+?\\)$" url) 210 | (setq pdfname (concat (match-string 1 url) ".pdf")) 211 | (setq newfile (read-file-name "save pdf as: " 212 | (expand-file-name arxiv-default-download-folder) 213 | nil nil pdfname)) 214 | (if (directory-name-p newfile) (setq newfile (concat newfile pdfname))) 215 | (url-copy-file url newfile 1) 216 | (unless confirm 217 | (setq input (read-char-exclusive (format "%s saved. Open pdf? (y/N) " newfile))) 218 | (when (or (equal input ?y) (equal input ?Y)) 219 | (funcall arxiv-pdf-open-function newfile))) 220 | newfile)) 221 | 222 | (defun arxiv-customize () 223 | "Customize the `arxiv-mode'." 224 | (interactive) 225 | (customize-group 'arxiv)) 226 | 227 | (defun arxiv-show-abstract () 228 | "Show the abstract window and display appropriate information." 229 | (unless (buffer-live-p arxiv-abstract-buffer) 230 | (setq arxiv-abstract-buffer (get-buffer-create "*arXiv-abstract*"))) 231 | (with-current-buffer arxiv-abstract-buffer 232 | (arxiv-abstract-mode) 233 | (setq-local prettify-symbols-alist arxiv-abstract-prettify-symbols-alist) 234 | (prettify-symbols-mode 1) 235 | (arxiv-format-abstract-page (nth arxiv-current-entry arxiv-entry-list))) 236 | (unless (window-live-p arxiv-abstract-window) 237 | (setq arxiv-abstract-window (display-buffer "*arXiv-abstract*" t))) 238 | (set-window-dedicated-p arxiv-abstract-window t)) 239 | 240 | (defun arxiv-toggle-abstract () 241 | "Toggle the visibility of the abstract. 242 | If the abstract window does not exist, create it and display 243 | appropriate content, otherwise kill it." 244 | (interactive) 245 | (if (window-live-p arxiv-abstract-window) 246 | (with-selected-window arxiv-abstract-window 247 | (delete-window) 248 | (setq arxiv-abstract-window nil)) 249 | (arxiv-show-abstract))) 250 | 251 | (defun arxiv-SPC () 252 | "Jump to the cursor position or open abstract window. 253 | If the cursor position does not correspond to the current entry, 254 | move the current entry to the corresponding position. Otherwise call 255 | `arxiv-toggle-abstract'." 256 | (interactive) 257 | (if (eq (/ (line-number-at-pos) 4) arxiv-current-entry) 258 | (arxiv-toggle-abstract) 259 | (arxiv-select-entry))) 260 | 261 | (defun arxiv-exit () 262 | "Exit from the arXiv mode, deleting all relevant buffers." 263 | (interactive) 264 | (when (window-live-p arxiv-abstract-window) 265 | (quit-restore-window arxiv-abstract-window 'kill) 266 | (setq arxiv-abstract-window nil)) 267 | (quit-restore-window (get-buffer-window "*arXiv-update*") 'kill) 268 | (when (get-buffer "*arXiv-abstract*") 269 | (kill-buffer "*arXiv-abstract*")) 270 | (setq arxiv-abstract-buffer nil) 271 | (setq arxiv-frame nil)) 272 | 273 | 274 | (defun arxiv-headerline-format () 275 | "Update the header line of *arxiv-update* buffer." 276 | (let* ((entry (format "%d/%d" (+ 1 arxiv-current-entry) arxiv-query-total-results)) 277 | (info-width (- (window-total-width) (length entry) 2))) 278 | (list 279 | (list (- info-width) 280 | (if (or (eq arxiv-mode-entry-function 'arxiv-read-new) (eq arxiv-mode-entry-function 'arxiv-read-recent)) 281 | arxiv-query-info 282 | (concat " Search results for " arxiv-query-info ". Sorted by: " arxiv-order-info)) 283 | (propertize " " 'display `(space :align-to ,info-width)) 284 | entry)))) 285 | 286 | (defun arxiv-fill-page (&optional min-entry max-entry) 287 | "Fill the details of the arxiv article list according to `arxiv-entry-list'. 288 | If MIN-ENTRY and MAX-ENTRY are ignored, defaults to fill with the whole `arxiv-entry-list'." 289 | (unless min-entry 290 | (setq min-entry 0)) 291 | (let ((arxiv-entry-list-trun (seq-subseq arxiv-entry-list min-entry max-entry))) 292 | (seq-doseq (entry arxiv-entry-list-trun) 293 | (arxiv-insert-with-face (format " %s\n " (alist-get 'title entry)) 'arxiv-title-face) 294 | (let ((author-list (copy-sequence (alist-get 'author entry))) (overlength nil)) 295 | (when (> (length author-list) arxiv-author-list-maximum) 296 | (setcdr (nthcdr (1- arxiv-author-list-maximum) author-list) nil) 297 | (setq overlength t)) 298 | (dolist (author author-list) 299 | (arxiv-insert-with-face (format "%s" author) 'arxiv-author-face) 300 | (insert ", ")) 301 | (if overlength 302 | (insert "et al.") 303 | (delete-char -2))) 304 | (let ((date (alist-get 'date entry))) 305 | (string-match "^[-[:digit:]]+ " date) 306 | (arxiv-insert-with-face (format "\n %s " (match-string 0 date)) 'arxiv-date-face)) 307 | (let ((cats (alist-get 'categories entry))) 308 | (dolist (cat cats) 309 | (arxiv-insert-with-face (format "[%s] " cat) 'arxiv-keyword-face))) 310 | (insert "\n\n")))) 311 | 312 | (defun arxiv-populate-page () 313 | "Populate the page of results according to `arxiv-entry-list' into buffer." 314 | (if arxiv-entry-list 315 | (progn 316 | (when arxiv-pop-up-new-frame 317 | (unless (frame-live-p arxiv-frame) 318 | (setq arxiv-frame 319 | (make-frame arxiv-frame-alist))) 320 | (select-frame-set-input-focus arxiv-frame)) 321 | (unless (buffer-live-p arxiv-buffer) 322 | (setq arxiv-buffer (get-buffer-create "*arXiv-update*"))) 323 | (with-current-buffer arxiv-buffer 324 | (setq buffer-read-only nil) 325 | (erase-buffer) 326 | (arxiv-fill-page) 327 | (goto-char (point-min)) 328 | (setq arxiv-current-entry 0) 329 | (arxiv-mode) 330 | (move-overlay arxiv-highlight-overlay 331 | (point) (progn (beginning-of-line 5) (point))) 332 | (goto-char (point-min)) 333 | (message "Showing results %d-%d of %d" arxiv-query-results-min arxiv-query-results-max arxiv-query-total-results)) 334 | 335 | (switch-to-buffer arxiv-buffer) 336 | (set-window-dedicated-p nil t) 337 | (when arxiv-startup-with-abstract-window 338 | (arxiv-show-abstract))) 339 | (message "No articles matching the search condition."))) 340 | 341 | (defun arxiv-show-next-page () 342 | "Perform one more query and fill the results into buffer. 343 | \(according to `arxiv-current-entry' and `arxiv-entries-per-fetch')" 344 | (unless arxiv-buffer 345 | (setq arxiv-buffer (get-buffer "*arXiv-update*"))) 346 | (let* ((min (* arxiv-entries-per-fetch (/ (+ 1 arxiv-current-entry) arxiv-entries-per-fetch))) 347 | (max (+ min arxiv-entries-per-fetch))) 348 | (when (> max arxiv-query-total-results) 349 | (setq max arxiv-query-total-results)) 350 | (message "Fetching results %d-%d..." (+ 1 min) max) 351 | (cond 352 | ((eq arxiv-mode-entry-function 'arxiv-read-new) 353 | (setq arxiv-entry-list 354 | (append arxiv-entry-list 355 | (arxiv-query (alist-get 'category arxiv-query-data-list) 356 | (alist-get 'date-start arxiv-query-data-list) 357 | (alist-get 'date-end arxiv-query-data-list) 358 | min t)))) 359 | ((eq arxiv-mode-entry-function 'arxiv-read-recent) 360 | (setq arxiv-entry-list 361 | (append arxiv-entry-list 362 | (arxiv-query (alist-get 'category arxiv-query-data-list) 363 | (alist-get 'date-start arxiv-query-data-list) 364 | (alist-get 'date-end arxiv-query-data-list) 365 | min nil)))) 366 | (t 367 | (setq arxiv-entry-list (append arxiv-entry-list (arxiv-query-general min))))) 368 | (set-buffer arxiv-buffer) 369 | (goto-char (point-max)) 370 | (setq buffer-read-only nil) 371 | (arxiv-fill-page min) 372 | (setq buffer-read-only t))) 373 | 374 | (defun arxiv-format-abstract-page (entry) 375 | "Format the arxiv abstract page according to ENTRY." 376 | ;; header-line 377 | (setq header-line-format (format " arXiv:%s" (cdr (assoc 'id entry)))) 378 | ;;contents 379 | (let ((buffer-read-only nil)) 380 | (erase-buffer) 381 | ;; title 382 | (arxiv-insert-with-face "\n" arxiv-title-face) 383 | (insert-button (format "%s" (cdr (assoc 'title entry))) 384 | 'action (lambda () (arxiv-open-current-url)) 385 | 'face 'arxiv-abstract-title-face 386 | 'mouse-face 'highlight 387 | 'follow-link t 388 | 'help-echo (format "Link: %s" (cdr (assoc 'url entry)))) 389 | (arxiv-insert-with-face "\n\n" arxiv-title-face) 390 | ;; author list 391 | (let ((author-list (cdr (assoc 'author entry)))) 392 | (dolist (author author-list) 393 | (insert-button (format "%s" author) 394 | 'action (lambda () (arxiv-toggle-abstract) (arxiv-read-author author)) 395 | 'follow-link t 396 | 'face 'arxiv-abstract-author-face 397 | 'mouse-face 'highlight 398 | 'help-echo (format "Look up author: %s" author)) 399 | (insert ", "))) 400 | (delete-char -2) 401 | (insert "\n\n") 402 | ;; abstract 403 | (let ((abstract (cdr (assoc 'abstract entry)))) 404 | (arxiv-insert-with-face " " arxiv-abstract-face) 405 | (setq abstract (replace-regexp-in-string "^ +" "" abstract)) 406 | (insert (propertize abstract 'font-lock-face arxiv-abstract-face 'wrap-prefix " "))) 407 | ;; highlight math 408 | (save-excursion 409 | (while (search-backward-regexp "\\$[^$]+\\$" nil t) 410 | (add-text-properties (match-beginning 0) (match-end 0) '(font-lock-face arxiv-abstract-math-face)))) 411 | ;; comment 412 | (if (cdr (assoc 'comment entry)) 413 | (arxiv-insert-with-face (format "\n\nComments: %s" (cdr (assoc 'comment entry))) arxiv-subfield-face) 414 | (arxiv-insert-with-face "\n\nComments: N/A" arxiv-subfield-face)) 415 | ;; subject 416 | (arxiv-insert-with-face (format "\nSubjects: ") arxiv-subfield-face) 417 | (let* ((main-cat t) (cats (cdr (assoc 'categories entry)))) 418 | (dolist (cat cats) 419 | (let (field) 420 | (setq field (cdr (assoc (intern-soft cat) arxiv-subject-classifications))) 421 | (if main-cat 422 | (progn ; the main subject is in bold 423 | (arxiv-insert-with-face (format "%s " field) '(:inherit arxiv-subfield-face :weight semi-bold)) 424 | (arxiv-insert-with-face (format "(%s)" cat) '(:inherit arxiv-subfield-face :weight semi-bold)) 425 | (setq main-cat nil)) 426 | (insert (propertize (format "%s " field) 'font-lock-face arxiv-subfield-face 'wrap-prefix " ")) 427 | (insert (propertize (format "(%s)" cat) 'font-lock-face arxiv-subfield-face 'wrap-prefix " "))) 428 | (arxiv-insert-with-face "; " arxiv-subfield-face)))) 429 | (delete-char -2) 430 | ;; journal/DOI 431 | (when (cdr (assoc 'journal entry)) (arxiv-insert-with-face (format "\nJournal: %s" (cdr (assoc 'journal entry))) arxiv-subfield-face)) 432 | (when (cdr (assoc 'DOI entry)) (arxiv-insert-with-face (format "\nDOI: %s" (cdr (assoc 'DOI entry))) arxiv-subfield-face)) 433 | ;; times 434 | (arxiv-insert-with-face (format "\nSubmitted: %s" (cdr (assoc 'date entry))) arxiv-subfield-face) 435 | (arxiv-insert-with-face (format "\nUpdated: %s" (cdr (assoc 'updated entry))) arxiv-subfield-face))) 436 | 437 | (defun arxiv-export-bibtex-to-string (&optional pdfpath) 438 | "Generate a bibtex entry according to the current arxiv entry. 439 | Also add a link to PDFPATH in bibtex entry if it is specified. 440 | It returns a string buffer containing the bibtex entry. This 441 | function is a part of arXiv mode, and is supposed to be called by 442 | `arxiv-export-bibtex' or `arxiv-export-bibtex-to-buffer'." 443 | (let* 444 | ((entry (nth arxiv-current-entry arxiv-entry-list)) 445 | (title (cdr (assoc 'title entry))) 446 | (id (cdr (assoc 'id entry))) 447 | (author-list (cdr (assoc 'author entry))) 448 | (abstract (cdr (assoc 'abstract entry))) 449 | (year (cdr (assoc 'date entry))) 450 | (url (cdr (assoc 'url entry))) 451 | (journal (cdr (assoc 'journal entry))) 452 | (doi (cdr (assoc 'doi entry))) 453 | (author) (key) (bibtex-info)) 454 | (setq author-list (mapcar (lambda (name) (progn 455 | (string-match "\\(.+\\) \\([^ ]+?\\)$" name) 456 | (setq name (concat (match-string 2 name) ", " (match-string 1 name))))) 457 | author-list)) 458 | (string-match "^[0-9]+" year) 459 | (setq year (match-string 0 year)) 460 | (setq author (mapconcat #'identity author-list " and ")) 461 | (setq abstract (replace-regexp-in-string "^ +" "" abstract)) 462 | (setq abstract (replace-regexp-in-string " +$" "" abstract)) 463 | (setq bibtex-info (format "@article{, 464 | title = {%s}, 465 | author = {%s}, 466 | year = {%s} 467 | }" title author year)) 468 | (with-temp-buffer 469 | (insert bibtex-info) 470 | (bibtex-mode) 471 | (bibtex-set-dialect 'BibTeX t) 472 | (setq key (bibtex-generate-autokey))) 473 | (setq bibtex-info (format "@article{%s, 474 | title = {%s}, 475 | author = {%s}, 476 | abstract = {%s}, 477 | archivePrefix = {arXiv}, 478 | eprint = {%s}, 479 | url = {%s}, 480 | year = {%s}" key title author abstract id url year)) 481 | (when doi 482 | (setq bibtex-info (concat bibtex-info (format ",\ndoi = {%s}" doi)))) 483 | (when journal 484 | (setq bibtex-info (concat bibtex-info (format ",\njournal = {%s}" journal)))) 485 | (when pdfpath 486 | (setq bibtex-info (concat bibtex-info (format ",\nfile = {:%s:pdf}" (expand-file-name pdfpath))))) 487 | (setq bibtex-info (concat bibtex-info "\n}")) 488 | bibtex-info)) 489 | 490 | (defun arxiv-export-bibtex (&optional pdfpath) 491 | "Add a new bibtex item to a .bib file according to the current arxiv entry. 492 | Also add a link to PDFPATH in bibtex entry if it is specified. 493 | This function is a part of arXiv mode. You can 494 | customize the default .bib file by customizing the 495 | `arxiv-default-bibliography' variable." 496 | (interactive) 497 | (let ((bibtex-info (arxiv-export-bibtex-to-string pdfpath)) 498 | (bibfile (read-file-name "export to bibliography file: " (expand-file-name arxiv-default-bibliography) nil 'confirm))) 499 | (with-temp-file bibfile 500 | (insert-file-contents bibfile) 501 | (goto-char (point-max)) 502 | (when (not (looking-at "^")) (insert "\n")) 503 | (insert bibtex-info) 504 | (goto-char (point-max)) 505 | (when (not (looking-at "^")) (insert "\n"))) 506 | (message (format "Written bibTeX entry to %s." bibfile)))) 507 | 508 | (defun arxiv-export-bibtex-to-buffer (&optional pdfpath) 509 | "Export bibtex for the current entry and display it in a temporary buffer. 510 | Also add a link to PDFPATH in bibtex entry if it is specified. 511 | This function is a part of arXiv mode, and 512 | is not related to the arxiv-add-bibtex-entry in org-ref package." 513 | (interactive) 514 | (let 515 | ((bibtex-info (arxiv-export-bibtex-to-string pdfpath))) 516 | (select-window arxiv-abstract-window) 517 | (pop-to-buffer "*arXiv-bibTeX*" '(display-buffer-below-selected)) 518 | ;; (split-window-below) 519 | ;; (select-window (next-window)) 520 | ;; (pop-to-buffer-same-window "*arXiv-bibTeX*") 521 | (erase-buffer) 522 | (insert bibtex-info) 523 | (setq buffer-read-only nil) 524 | (bibtex-mode) 525 | (bibtex-set-dialect 'BibTeX t))) 526 | 527 | (defun arxiv-download-pdf-export-bibtex () 528 | "Download the pdf file of the current entry and export a bibtex entry to the selected bibtex file." 529 | (interactive) 530 | (arxiv-export-bibtex (arxiv-download-pdf t))) 531 | 532 | 533 | ;; Entry functions 534 | 535 | ;;;###autoload 536 | (defun arxiv-read-new (&optional cat res-time) 537 | "Read new (submitted in the previous work day) arXiv articles in given CAT. 538 | If CAT is not supplied, prompt user for category. 539 | With optional arg RES-TIME, read new submission with respective to it." 540 | (interactive) 541 | (let* 542 | ((time (or res-time (current-time))) 543 | (TZ "EST+5EDT,M3.2.0/2,M11.1.0/2") ;; arXiv (Cornell) is based on eastern time (ET) 544 | (day (string-to-number (format-time-string "%u" time TZ))) 545 | (date-start nil) 546 | (date-end nil) 547 | (dayname-start "") 548 | (dayname-end "") 549 | (category (or cat 550 | (completing-read "Select category: " arxiv-categories nil t nil nil arxiv-default-category)))) 551 | 552 | ;; arxiv announces new submissions on 20:00 ET. Check if it's 20:00 yet. 553 | (when (< (string-to-number (format-time-string "%H" time TZ)) 20) 554 | (setq day (- day 1)) 555 | (setq time (time-subtract time 86400))) 556 | 557 | ;; new submission deadlines are 14:00 ET 558 | (let* ((dectime (decode-time time TZ)) 559 | (daysec (+ (* (decoded-time-hour dectime) 3600) (* (decoded-time-minute dectime) 60) (decoded-time-second dectime))) 560 | (timediff (- daysec (* 14 3600)))) 561 | (setq time (time-subtract time timediff))) 562 | 563 | ;; time for submissions are listed in UTC 564 | (cond 565 | ((equal day 5) ; Friday 566 | (progn 567 | (setq date-start (format-time-string "%Y%m%d%H%M" (time-subtract time (* 2 86400)) "UTC")) 568 | (setq date-end (format-time-string "%Y%m%d%H%M" (time-subtract time 86400) "UTC")) 569 | (setq dayname-start "Wed") 570 | (setq dayname-end "Thu"))) 571 | ((equal day 6) ; Saturday 572 | (progn 573 | (setq date-start (format-time-string "%Y%m%d%H%M" (time-subtract time (* 3 86400)) "UTC")) 574 | (setq date-end (format-time-string "%Y%m%d%H%M" (time-subtract time (* 2 86400)) "UTC")) 575 | (setq dayname-start "Wed") 576 | (setq dayname-end "Thu"))) 577 | ((or (equal day 7) (eq day 0)) ; Sunday 578 | (progn 579 | (setq date-start (format-time-string "%Y%m%d%H%M" (time-subtract time (* 3 86400)) "UTC")) 580 | (setq date-end (format-time-string "%Y%m%d%H%M" (time-subtract time (* 2 86400)) "UTC")) 581 | (setq dayname-start "Thu") 582 | (setq dayname-end "Fri"))) 583 | ((equal day 1) ; Monday 584 | (progn 585 | (setq date-start (format-time-string "%Y%m%d%H%M" (time-subtract time (* 3 86400)) "UTC")) 586 | (setq date-end (format-time-string "%Y%m%d%H%M" time "UTC")) 587 | (setq dayname-start "Fri") 588 | (setq dayname-end "Mon"))) 589 | (t ; Tue - Thu, read from previous day 590 | (progn 591 | (setq date-start (format-time-string "%Y%m%d%H%M" (time-subtract time 86400) "UTC")) 592 | (setq date-end (format-time-string "%Y%m%d%H%M" time "UTC")) 593 | (setq dayname-start (format-time-string "%a" (time-subtract time 86400) "UTC")) 594 | (setq dayname-end (format-time-string "%a" time "UTC"))))) 595 | ;; day to week name 596 | (setq arxiv-query-info (format " Showing new submissions in %s from %s(%s) to %s(%s)." 597 | category (substring date-start 0 8) dayname-start (substring date-end 0 8) dayname-end)) 598 | (setq arxiv-entry-list (arxiv-query category date-start date-end nil t)) 599 | (setq arxiv-query-data-list `((date-start . ,date-start) (date-end . ,date-end) (category . ,category))) 600 | (setq arxiv-mode-entry-function 'arxiv-read-new) 601 | (arxiv-query-sort-cat category) 602 | (if arxiv-entry-list 603 | (arxiv-populate-page) 604 | (when (y-or-n-p "Could not find any new submissions (this often happens because there is often a slight delay between arXiv's said and actual announcement time). 605 | Read from previous day instead? ") 606 | (arxiv-read-new category (time-subtract (current-time) 86400)))))) 607 | 608 | ;;;###autoload 609 | (defun arxiv-read-recent () 610 | "Read recent (past week) submissions of arXiv in a given category." 611 | (interactive) 612 | (let* 613 | ((date-end (format-time-string "%Y%m%d" (current-time))) 614 | (date-start (format-time-string "%Y%m%d" (time-subtract (current-time) (* 7 86400)))) 615 | (category (completing-read "Select category: " 616 | arxiv-categories nil t nil nil arxiv-default-category))) 617 | (setq arxiv-query-info (format " Showing recent submissions in %s in the past week (%s to %s)." category date-start date-end)) 618 | (setq date-start (concat date-start "0000")) 619 | (setq date-end (concat date-end "0000")) 620 | (setq arxiv-entry-list (arxiv-query category date-start date-end)) 621 | (setq arxiv-query-data-list `((date-start . ,date-start) (date-end . ,date-end) (category . ,category))) 622 | (setq arxiv-mode-entry-function 'arxiv-read-recent) 623 | (arxiv-populate-page))) 624 | 625 | ;;;###autoload 626 | (defun arxiv-read-author (&optional author) 627 | "Find the papers by author name in category supplied by user. 628 | If AUTHOR is non-nil, find papers by author in all categories." 629 | (interactive) 630 | (let ((category nil)) 631 | (if author 632 | (setq arxiv-query-data-list `((author t ,author)) 633 | arxiv-query-info (format "au:%s" author)) 634 | (setq author (read-string "Authors name (use space to separate): ") 635 | category (completing-read "Select category: " arxiv-categories nil t nil nil arxiv-default-category)) 636 | (setq arxiv-query-data-list `((author t ,author) (category t ,category)) 637 | arxiv-query-info (format "author:%s+category:%s" author category))) 638 | (setq arxiv-order-info "Submission date (newest first)" 639 | arxiv-query-sorting '(:sortby submittedDate :sortorder descending)) 640 | (setq arxiv-entry-list (arxiv-query-general)) 641 | (setq arxiv-mode-entry-function 'arxiv-read-author) 642 | (arxiv-populate-page))) 643 | 644 | ;;;###autoload 645 | (defun arxiv-search () 646 | "Do a simple search on arXiv datebase and list the result in buffer." 647 | (interactive) 648 | (let ((condition (read-string "Search all fields (use space to seperate and \"\" to quote): "))) 649 | (if (string-match "^ *$" condition) 650 | (message "exit with blank search condition.") 651 | (setq arxiv-query-data-list `((all t ,condition))) 652 | (setq arxiv-query-info (format "all:%s" condition)) 653 | (setq arxiv-query-sorting nil) 654 | (setq arxiv-order-info "Default") 655 | (setq arxiv-entry-list (arxiv-query-general)) 656 | (setq arxiv-mode-entry-function 'arxiv-search) 657 | (arxiv-populate-page)))) 658 | 659 | ;;;###autoload 660 | (defun arxiv-complex-search () 661 | "Do a complex search on arXiv database and list the result in buffer." 662 | (interactive) 663 | (setq arxiv-query-data-list nil) 664 | (setq arxiv-query-info "") 665 | (setq arxiv-query-sorting nil) 666 | (setq arxiv-order-info "Default") 667 | (arxiv-search-menu/body)) 668 | 669 | (defun arxiv-refine-search () 670 | "Refine search conditions in the *arXiv-update* buffer." 671 | (interactive) 672 | (if (or (eq arxiv-mode-entry-function 'arxiv-complex-search) (eq arxiv-mode-entry-function 'arxiv-search)) 673 | (progn 674 | (message "refine search condition: ") 675 | (arxiv-search-menu/body)) 676 | (message "Refining search is only available in arxiv-search or arxiv-complex-search."))) 677 | 678 | (defun arxiv-query-data-update (field condition) 679 | "Update the variable `arxiv-query-data-list' in FIELD. 680 | Do exclusive update if CONDITION is nil. Also updates `arxiv-query-info'." 681 | (if (or condition arxiv-query-data-list) 682 | (let ((temp-query-info) (context)) 683 | (if condition 684 | (setq temp-query-info "+") 685 | (setq temp-query-info "-")) 686 | (cond 687 | ((eq field 'all) 688 | (progn 689 | (setq context (read-string "Search all fields (use space to seperate and \"\" to quote): ")) 690 | (setq temp-query-info (concat temp-query-info "all:" context)))) 691 | ((eq field 'id) 692 | (progn 693 | (setq context (read-string "Article ID: ")) 694 | (setq temp-query-info (concat temp-query-info "ID:" context)))) 695 | ((eq field 'author) 696 | (progn 697 | (setq context (read-string "Authors name (use space to seperate): ")) 698 | (setq temp-query-info (concat temp-query-info "author:" context)))) 699 | ((eq field 'abstract) 700 | (progn 701 | (setq context (read-string "Abstract keywords (use space to seperate and \"\" to quote): ")) 702 | (setq temp-query-info (concat temp-query-info "abstract:" context)))) 703 | ((eq field 'category) 704 | (progn 705 | (setq context (completing-read "Category: " arxiv-categories nil t nil nil arxiv-default-category)) 706 | (setq temp-query-info (concat temp-query-info "category:" context)))) 707 | ((eq field 'journal) 708 | (progn 709 | (setq context (read-string "Journal: ")) 710 | (setq temp-query-info (concat temp-query-info "journal:" context)))) 711 | ((eq field 'comment) 712 | (progn 713 | (setq context (read-string "Search comments (use space to seperate and \"\" to quote): ")) 714 | (setq temp-query-info (concat temp-query-info "comment:" context)))) 715 | ((eq field 'time) 716 | (let 717 | ((date-min (read-string "Enter starting date (YYYYMMDD, ex: 19910101): ")) 718 | (date-max (read-string "Enter ending date (YYYYMMDD, ex: 19910101): "))) 719 | (setq context (format "[%s0000+TO+%s0000]" date-min date-max)) 720 | (setq temp-query-info (concat temp-query-info "time:" (format "%s-%s" date-min date-max)))))) 721 | (if (string-match "^ *$" context) 722 | (message "Void search argument.") 723 | (setq arxiv-query-info (concat arxiv-query-info temp-query-info)) 724 | (setq arxiv-query-data-list (cons (list field condition context) arxiv-query-data-list)))) ; this reversed the order of the list, need to fix it later on 725 | (message "Only inclusive searching is allowed as the first keyword.")) 726 | (arxiv-search-menu/body)) 727 | 728 | (defun arxiv-query-order-update () 729 | "Update the variable `arxiv-query-sorting from user input." 730 | (setq arxiv-order-info 731 | (completing-read "Sort results by: " 732 | '("Announcement date (newest first)" 733 | "Announcement date (oldest first)" 734 | "Submission date (newest first)" 735 | "Submission date (oldest first)" 736 | "Relevance") 737 | nil t)) 738 | (cond 739 | ((equal arxiv-order-info "Relevance") 740 | (setq arxiv-query-sorting '(:sortby relevance :sortorder descending))) 741 | ((equal arxiv-order-info "Announcement date (newest first)") 742 | (setq arxiv-query-sorting '(:sortby lastUpdatedDate :sortorder descending))) 743 | ((equal arxiv-order-info "Announcement date (oldest first)") 744 | (setq arxiv-query-sorting '(:sortby lastUpdatedDate :sortorder ascending))) 745 | ((equal arxiv-order-info "Submission date (newest first)") 746 | (setq arxiv-query-sorting '(:sortby submittedDate :sortorder descending))) 747 | ((equal arxiv-order-info "Submission date (oldest first)") 748 | (setq arxiv-query-sorting '(:sortby submittedDate :sortorder ascending)))) 749 | (arxiv-search-menu/body)) 750 | 751 | (defun arxiv-hydra-perform-search () 752 | "Helper function for `arxiv-search-menu'." 753 | (interactive) 754 | (if arxiv-query-data-list 755 | (progn 756 | (setq arxiv-query-info (replace-regexp-in-string "^+" "" arxiv-query-info)) 757 | (setq arxiv-query-data-list (nreverse arxiv-query-data-list)) ; fix the reverse order caused in arxiv-query-data-update () 758 | (setq arxiv-entry-list (arxiv-query-general)) 759 | (setq arxiv-mode-entry-function 'arxiv-complex-search) 760 | (arxiv-populate-page)) 761 | (message "quit with blank search conditions"))) 762 | 763 | 764 | (defhydra arxiv-search-menu (:color blue :foreign-keys warn :exit t) 765 | " 766 | Condition: %`arxiv-query-info 767 | Sort by: %s(format arxiv-order-info) 768 | Add search condition: 769 | ------------------------------------------------------------------------------- 770 | _a_: all _i_: article ID _t_: submitted time 771 | _u_: author(s) _b_: abstract _c_: category 772 | _j_: journal _m_: comment _s_: sorting 773 | _-_: exclude condition _x_: perform search with current condition(s) 774 | " 775 | ("a" (arxiv-query-data-update 'all t)) 776 | ("i" (arxiv-query-data-update 'id t)) 777 | ("t" (arxiv-query-data-update 'time t)) 778 | ("u" (arxiv-query-data-update 'author t)) 779 | ("b" (arxiv-query-data-update 'abstract t)) 780 | ("c" (arxiv-query-data-update 'category t)) 781 | ("j" (arxiv-query-data-update 'journal t)) 782 | ("m" (arxiv-query-data-update 'comment t)) 783 | ("s" (arxiv-query-order-update)) 784 | ("-" arxiv-search-menu-ex/body) 785 | ("x" arxiv-hydra-perform-search) 786 | ("q" (setq arxiv-query-data-list nil) "quit")) 787 | 788 | (defhydra arxiv-search-menu-ex (:color red :foreign-keys warn :exit t) 789 | " 790 | Condition: %`arxiv-query-info 791 | Sort by: %s(format arxiv-order-info) 792 | Exclude arXiv search condition: 793 | ------------------------------------------------------------------------------- 794 | _a_: all _i_: article ID _t_: submitted time 795 | _u_: author(s) _b_: abstract _c_: category 796 | _j_: journal _m_: comment _s_: sorting 797 | _+_: include condition _x_: perform search with current condition(s) 798 | " 799 | ("a" (arxiv-query-data-update 'all nil)) 800 | ("i" (arxiv-query-data-update 'id nil)) 801 | ("t" (arxiv-query-data-update 'time nil)) 802 | ("u" (arxiv-query-data-update 'author nil)) 803 | ("b" (arxiv-query-data-update 'abstract nil)) 804 | ("c" (arxiv-query-data-update 'category nil)) 805 | ("j" (arxiv-query-data-update 'journal nil)) 806 | ("m" (arxiv-query-data-update 'comment nil)) 807 | ("s" (arxiv-query-order-update)) 808 | ("+" arxiv-search-menu/body) 809 | ("x" arxiv-hydra-perform-search) 810 | ("q" (setq arxiv-query-data-list nil) "quit")) 811 | 812 | (defhydra arxiv-help-menu (:color red :foriegn-keys run) 813 | " 814 | ArXiv mode help message 815 | --------------------------------------------------------------------------------------------------------- 816 | _n_: next entry _SPC_: toggle abstract window _b_: export bibtex entry 817 | _p_: previous entry _RET_: open link in browser _B_: display bibtex entry in new buffer 818 | _r_: refine search _d_: download PDF _e_: download pdf & export bibtex entry 819 | _q_: quit Arxiv mode _?_: toggle this help 820 | " 821 | ("n" arxiv-next-entry) 822 | ("p" arxiv-prev-entry) 823 | ("r" arxiv-refine-search :exit t) 824 | ("q" arxiv-exit :exit t) 825 | ("SPC" arxiv-SPC) 826 | ("RET" arxiv-open-current-url) 827 | ("d" arxiv-download-pdf) 828 | ("?" nil) 829 | ("b" arxiv-export-bibtex) 830 | ("B" arxiv-export-bibtex-to-buffer) 831 | ("e" arxiv-download-pdf-export-bibtex)) 832 | 833 | (provide 'arxiv-mode) 834 | 835 | ;;; arxiv-mode.el ends here 836 | --------------------------------------------------------------------------------