├── .github ├── .aspell.en.pws ├── .typo-ci.yml ├── .wucuo.el └── workflows │ ├── ci.yml │ ├── eldev_lint.yml │ ├── spell_check_typoci.yml │ └── spell_check_wucuo.yml ├── .gitignore ├── Eldev ├── LICENSE ├── README.md ├── ess-view-data.el └── screenshot ├── ess-view-data-count.gif ├── ess-view-data-filter.gif ├── ess-view-data-log.png ├── ess-view-data-overview-skimr.gif ├── ess-view-data-print.png ├── ess-view-data-reset.gif ├── ess-view-data-select.gif ├── ess-view-data-sort.gif └── ess-view-data-summarise.gif /.github/.aspell.en.pws: -------------------------------------------------------------------------------- 1 | personal_ws-1.1 en 30 utf-8 2 | DT 3 | ESS 4 | FIXME 5 | MERCHANTABILITY 6 | Rhistory 7 | SAS 8 | Shuguang 9 | alist 10 | autoload 11 | csv 12 | dataname 13 | dataset 14 | datatable 15 | dired 16 | dplyr 17 | el 18 | emacs 19 | ess 20 | fread 21 | keymap 22 | mannualy 23 | rda 24 | rdata 25 | rds 26 | sas 27 | sesssion 28 | wdired 29 | xport 30 | xpt 31 | -------------------------------------------------------------------------------- /.github/.typo-ci.yml: -------------------------------------------------------------------------------- 1 | # This is a sample .typo-ci.yml file, it's used to configure how Typo CI will behave. 2 | # Add it to the root of your project and push it to github. 3 | --- 4 | 5 | # What language dictionaries should it use? By default Typo CI will select 'en' & 'en_GB' 6 | # Currently Typo CI supports: 7 | # de 8 | # en 9 | # en_GB 10 | # es 11 | # fr 12 | # it 13 | # nl 14 | # pt 15 | # pt_BR 16 | # tr 17 | dictionaries: 18 | - en 19 | - en_GB 20 | 21 | # Any files/folders we should ignore? 22 | excluded_files: 23 | - "vendor/**/*" 24 | - "node_modules/**/*" 25 | - "*.key" 26 | - "*.enc" 27 | - "*.min.css" 28 | - "*.css.map" 29 | - "*.min.js" 30 | - "*.js.map" 31 | - "*.mk" 32 | - "package-lock.json" 33 | - "yarn.lock" 34 | - "Gemfile.lock" 35 | - ".typo-ci.yml" 36 | - ".github/.typo-ci.yml" 37 | - "*.gif" 38 | - ".github/**/*" 39 | 40 | # Any words we should ignore? 41 | excluded_words: 42 | - Eldev 43 | - defun 44 | - elisp 45 | - emacs 46 | - ess-command 47 | - rutils 48 | - rutils-packrat-status 49 | - rutils-renv-status 50 | - rutils-send--command-with-project 51 | - package 52 | - renv 53 | - typoci 54 | - wucuo 55 | - defsubst 56 | 57 | # Would you like filenames to also be spellchecked? 58 | spellcheck_filenames: false 59 | -------------------------------------------------------------------------------- /.github/.wucuo.el: -------------------------------------------------------------------------------- 1 | (require 'wucuo) 2 | 3 | (add-to-list 'wucuo-exclude-directories ".github") 4 | (add-to-list 'wucuo-exclude-directories "wucuo") 5 | 6 | (setq ispell-personal-dictionary ".github/.aspell.en.pws") 7 | 8 | (setq-default wucuo-find-file-regexp "^.*\\.\\(el\\|md\\|org\\)") 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | - '**.gif' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | - '**.gif' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | emacs_version: 19 | - 27.2 20 | 21 | steps: 22 | - name: Setup GNU Emacs 23 | uses: purcell/setup-emacs@master 24 | with: 25 | version: ${{ matrix.emacs_version }} 26 | 27 | - name: Install Eldev 28 | run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh 29 | 30 | - name: Checkout 31 | uses: actions/checkout@v2 32 | 33 | - name: Test 34 | run: | 35 | eldev -p -dtT compile 36 | -------------------------------------------------------------------------------- /.github/workflows/eldev_lint.yml: -------------------------------------------------------------------------------- 1 | name: Eldevlint 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | - '**.gif' 8 | - '.aspell.en.pws' 9 | pull_request: 10 | paths-ignore: 11 | - '**.md' 12 | - '**.gif' 13 | - '.aspell.en.pws' 14 | - '.github' 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | emacs_version: 22 | - 27.2 23 | 24 | steps: 25 | - name: Setup GNU Emacs 26 | uses: purcell/setup-emacs@master 27 | with: 28 | version: ${{ matrix.emacs_version }} 29 | 30 | - name: Install Eldev 31 | run: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/github-eldev | sh 32 | 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: Test 37 | run: | 38 | eldev lint 39 | -------------------------------------------------------------------------------- /.github/workflows/spell_check_typoci.yml: -------------------------------------------------------------------------------- 1 | # Add to: .github/workflows/spellcheck.yml 2 | name: Typo CI 3 | 4 | on: 5 | push: 6 | branches-ignore: 7 | - master 8 | jobs: 9 | spellcheck: 10 | name: Typo CI (GitHub Action) 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 4 13 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 14 | steps: 15 | - name: TypoCheck 16 | uses: typoci/spellcheck-action@master 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/spell_check_wucuo.yml: -------------------------------------------------------------------------------- 1 | name: Wucuo 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.gif' 7 | pull_request: 8 | paths-ignore: 9 | - '**.gif' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | emacs_version: 18 | - 27.2 19 | 20 | steps: 21 | - uses: purcell/setup-emacs@master 22 | with: 23 | version: ${{ matrix.emacs_version }} 24 | 25 | - uses: actions/checkout@v2 26 | 27 | - name: Print emacs version 28 | run: emacs --version 29 | 30 | - name: Install dependency 31 | run: sudo apt install aspell aspell-en 32 | 33 | - name: Install Wucuo 34 | run: mkdir -p wucuo && 35 | curl -fsSL https://raw.githubusercontent.com/redguardtoo/wucuo/master/wucuo.el > wucuo/wucuo.el && 36 | curl -fsSL https://raw.githubusercontent.com/redguardtoo/wucuo/master/wucuo-sdk.el > wucuo/wucuo-sdk.el && 37 | curl -fsSL https://raw.githubusercontent.com/redguardtoo/wucuo/master/wucuo-flyspell-html-verify.el && 38 | curl -fsSL https://raw.githubusercontent.com/redguardtoo/wucuo/master/wucuo-flyspell-org-verify.el > wucuo/wucuo-flyspell-org-verify.el 39 | 40 | - name: Wucuo 41 | run: | 42 | emacs -batch -Q -L wucuo/ -l .github/.wucuo.el --eval '(let* ((ispell-program-name "aspell") (ispell-extra-args (wucuo-aspell-cli-args t))) (wucuo-spell-check-directory "." t))' 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled 2 | *.elc 3 | 4 | # Packaging 5 | .cask 6 | 7 | # Backup files 8 | *~ 9 | 10 | # Undo-tree save-files 11 | *.~undo-tree 12 | -------------------------------------------------------------------------------- /Eldev: -------------------------------------------------------------------------------- 1 | ; -*- mode: emacs-lisp; lexical-binding: t -*- 2 | 3 | (eldev-require-version "0.5") 4 | 5 | ;; For `let-alist' on older Emacs versions. 6 | (eldev-use-package-archive 'gnu) 7 | (eldev-use-package-archive 'melpa) 8 | 9 | (eldev-use-plugin 'autoloads) 10 | 11 | ;; Avoid including files in test "projects". 12 | (setf eldev-standard-excludes (append eldev-standard-excludes '("./test/*/"))) 13 | ;; (setf eldev-standard-excludes `(:or ,eldev-standard-excludes "org-roam-macs.el")) 14 | 15 | 16 | ;; Tell checkdoc not to demand two spaces after a period. 17 | ;; (setq sentence-end-double-space nil) 18 | ;; (setq-default indent-tabs-mode nil) 19 | 20 | ;; (setf eldev-lint-default '(elisp)) 21 | 22 | ;; ;; Optional dependencies, which we install so that we can test them 23 | ;; (eldev-add-extra-dependencies 'test 'ido-completing-read+) 24 | 25 | ;; (with-eval-after-load 'elisp-lint 26 | ;; ;; We will byte-compile with Eldev. 27 | ;; (setf elisp-lint-ignored-validators '("package-lint" "fill-column") 28 | ;; enable-local-variables :all)) 29 | 30 | ;; ;; Teach linter how to properly indent emacsql vectors 31 | ;; (eldev-add-extra-dependencies 'lint 'emacsql) 32 | ;; (add-hook 'eldev-lint-hook 33 | ;; (lambda () 34 | ;; (eldev-load-project-dependencies 'lint nil t) 35 | ;; (require 'emacsql) 36 | ;; (call-interactively #'emacsql-fix-vector-indentation))) 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/ess-view-data-badge.svg)](https://melpa.org/#/ess-view-data) 2 | [![MELPA Stable](https://stable.melpa.org/packages/ess-view-data-badge.svg)](https://stable.melpa.org/#/ess-view-data) 3 | [![Build Status](https://github.com/ShuguangSun/ess-view-data/workflows/CI/badge.svg)](https://github.com/ShuguangSun/ess-view-data/actions) 4 | [![License](http://img.shields.io/:license-gpl3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.html) 5 | 6 | # ess-view-data 7 | 8 | To do tidyverse-like view and manipulate data in ESS and R. 9 | 10 | ## Installation 11 | 12 | Clone this repository, or install from MELPA. Add the following to your `.emacs`: 13 | 14 | ``` elisp 15 | (require 'ess-view-data) 16 | ``` 17 | 18 | Call `ess-view-data-print`, select a object whichever can be convert to a tibble or data.table depending on the backend, and then a buffer will pop up with data listed/printed. Further verbs can be done, like filter, select/unselect, mutate, group/ungroup, count, unique, summarise, and etc. It can be reset (`ess-view-data-reset`) any time. 19 | 20 | To avoid mistaking break the original data, it will make a copy (e.g., `as_tibble(dt)` or `as.data.table(dt)`) as default. 21 | 22 | If data.table is preferred, just set `ess-view-data-current-backend` to `data.table+magrittr`. Call `ess-view-data-set-backend` to change the backends. 23 | 24 | It will put a head information at above: 25 | ```r 26 | # Trace: as_tibble(dt) %>% dplyr::filter(PARAMCD == "ORR", CYCLE == 1) 27 | # Last: %>% dplyr::filter(PARAMCD == "ORR", CYCLE == 1) 28 | # Page number: 1 / 1 29 | # A tibble: 73 x 19 30 | ``` 31 | 32 | - The 'Trace' line tracks the history of actions, and it can be copyed to the code after viewing. **NB** history of the operaitions can be found in buffer `*ESS*`. 33 | - The 'Last' line records the last verb. 34 | - The 'Page number' shows the current page/total number of pages. 35 | - The 'A tibble' show the class and how many rows and columns in the tibble. **NB**, the 'dplyr' backend copies the data.frame to be a tibble first. 36 | 37 | ## Customization 38 | 39 | ### ess-view-data-backend-list 40 | 41 | - dplyr (default) 42 | - dplyr+DT (out of Emacs, using DT) 43 | - data.table+magrittr 44 | 45 | ### ess-view-data-print-backend-list 46 | 47 | - print (default) 48 | - kable 49 | 50 | ### ess-view-data-save-backend-list 51 | 52 | - write.csv (default) 53 | - readr::write_csv 54 | - data.table::fwrite 55 | - kable 56 | 57 | ### ess-view-data-complete-backend-list 58 | 59 | - jsonlite 60 | 61 | ### ess-view-data-read-string 62 | 63 | - ess-completing-read (default) 64 | - completing-read 65 | - ido-completing-read 66 | - ivy-completing-read 67 | 68 | ### ess-view-data-tibble-crayon-enabled-p 69 | 70 | set `ess-view-data-tibble-crayon-enabled-p` to `t` (default) will enable crayon 71 | for tibble if `crayon.enabled` is set in the R's options. 72 | 73 | ## Usage 74 | 75 | **NOTE**: it will make a copy of the data and then does the following action 76 | 77 | The entry function to view data: 78 | - [x] ess-view-data-print 79 | 80 | In a ess-r buffer or a Rscript buffer, `M-x ess-view-data-print` and input `mtcars`, 81 | 82 | ![ess-view-data-print](screenshot/ess-view-data-print.png?raw=true) 83 | 84 | Setting: 85 | 86 | - [x] ess-view-data-set-backend: change backend 87 | - [x] ess-view-data-toggle-maxprint: toggle limitation of lines per page to print 88 | 89 | Verbs: 90 | 91 | - [x] ess-view-data-filter 92 | 93 | ![ess-view-data-filter](/screenshot/ess-view-data-filter.gif?raw=true) 94 | 95 | - [x] ess-view-data-select / ess-view-data-unselect 96 | 97 | ![ess-view-data-select](/screenshot/ess-view-data-select.gif?raw=true) 98 | 99 | - [x] ess-view-data-sort 100 | 101 | ![ess-view-data-sort](screenshot/ess-view-data-sort.gif?raw=true) 102 | 103 | - [x] ess-view-data-group / ess-view-data-ungroup 104 | - [x] ess-view-data-mutate 105 | - [x] ess-view-data-slice 106 | - [x] ess-view-data-wide2long / ess-view-data-long2wide 107 | - [x] ess-view-data-update 108 | - [x] ess-view-data-reset 109 | - [x] ess-view-data-unique 110 | - [x] ess-view-data-count 111 | 112 | ![ess-view-data-count](screenshot/ess-view-data-count.gif?raw=true) 113 | 114 | - [x] ess-view-data-summarise 115 | 116 | ![ess-view-data-summarise](screenshot/ess-view-data-summarise.gif?raw=true) 117 | 118 | - [x] ess-view-data-overview 119 | 120 | ![ess-view-data-overview-skimr](screenshot/ess-view-data-overview-skimr.gif) 121 | 122 | - [x] ess-view-data-goto-page / -next-page / -preious-page / -first-page / -last-page / -page-number 123 | - [x] ess-view-data-save 124 | 125 | Utitlities: 126 | 127 | 128 | In indirect buffer, for example, the buffer poped up when ess-view-data-filter is called 129 | 130 | - [x] ess-view-data-complete-object: complete or insert the name of one column/variable 131 | - [x] ess-view-data-complete-data: complete or insert the value of one column/variable 132 | - [x] ess-view-data-insert-all-cols: insert names of all columns/variables 133 | - [x] ess-view-data-insert-all-values: insert values of all columns/variables 134 | 135 | 136 | ## TODO 137 | 138 | - [ ] row.names support 139 | - [ ] header-line 140 | -------------------------------------------------------------------------------- /ess-view-data.el: -------------------------------------------------------------------------------- 1 | ;;; ess-view-data.el --- View Data -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2019-2023 Shuguang Sun 4 | 5 | ;; Author: Shuguang Sun 6 | ;; Created: 2019/04/06 7 | ;; Version: 1.3 8 | ;; URL: https://github.com/ShuguangSun/ess-view-data 9 | ;; Package-Requires: ((emacs "26.1") (ess "18.10.1") (csv-mode "1.12") (transient "0.3.7")) 10 | ;; Keywords: tools 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;; Customization: 28 | ;; ess-view-data-backend-list: dplyr (default), dplyr+DT, data.table+magrittr 29 | ;; ess-view-data-print-backend-list: print (default), kable 30 | ;; ess-view-data-save-backend-list: write.csv (default), readr::write_csv, 31 | ;; data.table::fwrite kable 32 | ;; ess-view-data-complete-backend-list: jsonlite 33 | ;; ess-view-data-read-string: ess-completing-read (default), completing-read, 34 | ;; ido-completing-read, ivy-completing-read 35 | 36 | ;; Utils: 37 | ;; NOTE: it will make a copy of the data and then does the following action 38 | ;; ess-view-data-print: the main function to view data 39 | 40 | ;; Example: In a ess-r buffer or a Rscript buffer, `M-x ess-view-data-print` 41 | ;; and input `mtcars`. 42 | 43 | ;; ess-view-data-set-backend: change backend 44 | ;; ess-view-data-toggle-maxprint: toggle limitation of lines per page to print 45 | 46 | ;; ess-view-data-verbs 47 | 48 | ;; Example: In the ESS-V buffer, `M-x ess-view-data-verbs` and select the verb 49 | ;; to do with. 50 | 51 | ;; ess-view-data-filter 52 | 53 | ;; Example: In the ESS-V buffer, `M-x ess-view-data-filter`, `cyl mpg` to 54 | ;; select columns and to finish input. An indirect buffer pops up and 55 | ;; 'data-masking' Expressions can be edited. 56 | 57 | ;; ess-view-data-select / ess-view-data-unselect 58 | 59 | ;; Example: In the ESS-V buffer, `M-x ess-view-data-select`, `cyl mpg` to 60 | ;; select columns and to finish input. 61 | 62 | ;; ess-view-data-sort 63 | ;; ess-view-data-group / ess-view-data-ungroup 64 | ;; ess-view-data-mutate 65 | ;; ess-view-data-slice 66 | ;; ess-view-data-wide2long / ess-view-data-long2wide 67 | ;; ess-view-data-update 68 | ;; ess-view-data-reset 69 | 70 | ;; Example: In the ESS-V buffer, `M-x ess-view-data-reset`, an indirect buffer 71 | ;; pops up and the action history can be edited. 72 | 73 | ;; ess-view-data-unique 74 | ;; ess-view-data-count 75 | 76 | ;; Example: In the ESS-V buffer, `M-x ess-view-data-count`, `cyl mpg` to 77 | ;; select columns and to finish input. In the updated buffer with count 78 | ;; information, `M-x ess-view-data-print` to go back. 79 | 80 | ;; ess-view-data-summarise 81 | ;; ess-view-data-overview 82 | ;; ess-view-data-goto-page / -next-page / -previous-page / -first-page / 83 | ;; -last-page / -page-number 84 | ;; ess-view-data-save 85 | 86 | ;;; Code: 87 | 88 | ;; (require 'eieio) 89 | 90 | (eval-when-compile (require 'cl-lib)) 91 | (eval-when-compile (require 'cl-generic)) 92 | 93 | (require 'ess-inf) 94 | (require 'ess-rdired) 95 | (require 'ess-r-mode) 96 | (require 'ess-r-completion) 97 | (require 'subr-x) 98 | (require 'json) 99 | (require 'transient) 100 | 101 | (defgroup ess-view-data () 102 | "ess-view-dat" 103 | :group 'ess 104 | :prefix "ess-view-data-") 105 | 106 | (defcustom ess-view-data-buffer-name-format "*R Data View: %1$s (%2$s)*" 107 | "Buffer name for R data, with two parameter: variable name, proc-name." 108 | :type 'string 109 | :group 'ess-view-data) 110 | 111 | (defcustom ess-view-data-source-buffer-name-format "*R Data View Edit: %s*" 112 | "Buffer for R data." 113 | :type 'string 114 | :group 'ess-view-data) 115 | 116 | ;; FIXME: r symbol name 117 | (defcustom ess-view-data-objname-regex "^[^a-zA-Z]\\|[^.a-zA-Z0-9]+" 118 | "Object name needs to be back quoted." 119 | :type 'string 120 | :group 'ess-view-data) 121 | 122 | (defcustom ess-view-data-options-width 5000 123 | "Width to print data: options(width= `ess-view-data-options-width')." 124 | :type 'integer 125 | :group 'ess-view-data) 126 | 127 | (defcustom ess-view-data-rows-per-page 200 128 | "Rows per page." 129 | :type 'integer 130 | :group 'ess-view-data) 131 | 132 | (defcustom ess-view-data-show-code t 133 | "Show code on top of the view data buffer." 134 | :type 'boolean 135 | :group 'ess-view-data) 136 | 137 | (defcustom ess-view-data-show-no-page-number t 138 | "Not to show page number on top of the view data buffer." 139 | :type 'boolean 140 | :group 'ess-view-data) 141 | 142 | (defcustom ess-view-data-write-dribble t 143 | "Write to dribble for tracking." 144 | :type 'boolean 145 | :group 'ess-view-data) 146 | 147 | (defcustom ess-view-data-tibble-crayon-enabled-p nil 148 | "Whether to enable crayon for tibble. 149 | 150 | If enabled, `ansi-color-for-comint-mode-on' should be turn on." 151 | :type 'boolean 152 | :group 'ess-view-data) 153 | 154 | 155 | (defvar ess-view-data-backend-list 156 | (list 'dplyr 'dplyr+DT 'data.table+magrittr) 157 | "List of backends.") 158 | 159 | (defcustom ess-view-data-current-backend 'dplyr 160 | "The ess-view-data backend in using." 161 | :type `(choice ,@(mapcar (lambda (x) 162 | `(const :tag ,(symbol-name x) ,x)) 163 | ess-view-data-backend-list) 164 | (symbol :tag "Other")) 165 | :group 'ess-view-data) 166 | 167 | 168 | (defvar ess-view-data-print-backend-list 169 | (list 'print 'kable) 170 | "List of backends.") 171 | 172 | 173 | (defcustom ess-view-data-current-update-print-backend 'print 174 | "The ess-view-data backend in using." 175 | :type `(choice ,@(mapcar (lambda (x) 176 | `(const :tag ,(symbol-name x) ,x)) 177 | ess-view-data-print-backend-list) 178 | (symbol :tag "Other")) 179 | :group 'ess-view-data) 180 | 181 | (defcustom ess-view-data-current-summarize-print-backend 'kable 182 | "The ess-view-data backend in using." 183 | :type `(choice ,@(mapcar (lambda (x) 184 | `(const :tag ,(symbol-name x) ,x)) 185 | ess-view-data-print-backend-list) 186 | (symbol :tag "Other")) 187 | :group 'ess-view-data) 188 | 189 | 190 | (defvar ess-view-data-save-backend-list 191 | (list 'write.csv 'readr::write_csv 'data.table::fwrite 'kable) 192 | "List of backends for write data to csv.") 193 | 194 | (defcustom ess-view-data-current-save-backend 'write.csv 195 | "The backend to save data." 196 | :type `(choice ,@(mapcar (lambda (x) 197 | `(const :tag ,(symbol-name x) ,x)) 198 | ess-view-data-save-backend-list) 199 | (symbol :tag "Other")) 200 | :group 'ess-view-data) 201 | 202 | (defvar ess-view-data-complete-backend-list 203 | (list 'jsonlite) 204 | "List of backends to read completion list.") 205 | 206 | (defcustom ess-view-data-current-complete-backend 'jsonlite 207 | "The backend to save data." 208 | :type `(choice ,@(mapcar (lambda (x) 209 | `(const :tag ,(symbol-name x) ,x)) 210 | ess-view-data-complete-backend-list) 211 | (symbol :tag "Other")) 212 | :group 'ess-view-data) 213 | 214 | 215 | (defcustom ess-view-data-read-string 'ess-completing-read 216 | "The function used to completing read." 217 | :type `(choice (const :tag "ESS" ess-completing-read) 218 | (const :tag "basic" completing-read) 219 | (const :tag "ido" ido-completing-read) 220 | (const :tag "ivy" ivy-completing-read) 221 | (function :tag "Other")) 222 | :group 'ess-view-data) 223 | 224 | (defcustom ess-view-data-auto-show-transient nil 225 | "Whether to automatically show the transient menu when opening an ess-view-data buffer." 226 | :type 'boolean 227 | :group 'ess-view-data) 228 | 229 | 230 | ;; TODO: configure input functions here 231 | (defvar ess-view-data-backend-setting 232 | '((dplyr . (:desc "desc(%s)" :slice "pos, like 1, 1:5, n(): ")) 233 | (dplyr+DT . (:desc "desc(%s)" :slice "pos, like 1, 1:5, n(): ")) 234 | (data.table+magrittr . (:desc "-%s" :slice "pos, like 1, 1:5, .N: "))) 235 | "List of backends.") 236 | 237 | (defvar ess-view-data-verb-update-list 238 | (list "select" "unselect" "sort" "group" "ungroup" "slice") 239 | "List of verbs which can change the data.") 240 | 241 | (defvar ess-view-data-verb-update-indirect-list 242 | (list "filter" "mutate" "transmute" 243 | "wide2long" "long2wide" "wide2long-pivot_longer" "long2wide-pivot_wider") 244 | "List of verbs which can change the data.") 245 | 246 | (defvar ess-view-data-verb-summarise-list 247 | (list "count" "unique" "slice" "summarise" "skimr" "skimr-all") 248 | "List of verbs which do summarise.") 249 | 250 | (defvar ess-view-data-verb-summarise-indirect-list 251 | (list "count" "unique" "slice" "summarise") 252 | "List of verbs which do summarise.") 253 | 254 | (defvar-local ess-view-data-object nil 255 | "Cache of object name.") 256 | 257 | (defvar-local ess-view-data-temp-object nil 258 | "Temporary variable for ess-view-data.") 259 | 260 | (defvar ess-view-data-temp-object-list '() 261 | "List of temporary variable for ess-view-data.") 262 | 263 | (defvar-local ess-view-data-maxprint-p nil 264 | "Whether to print all data in one page.") 265 | 266 | (defvar-local ess-view-data-page-number 0 267 | "Current page number - 1.") 268 | 269 | (defvar-local ess-view-data-total-page 1 270 | "Total page number.") 271 | 272 | 273 | (defvar-local ess-view-data-history nil 274 | "The history of operations.") 275 | 276 | (defvar-local ess-view-data-completion-object nil 277 | "The candidate for completion.") 278 | 279 | (defvar-local ess-view-data-completion-candidate nil 280 | "The candidate for completion.") 281 | 282 | 283 | 284 | 285 | (defvar ess-view-data-mode-map 286 | (let ((keymap (make-sparse-keymap))) 287 | (define-key keymap (kbd "C-c C-p") #'ess-view-data-print-ex) 288 | (define-key keymap (kbd "C-c C-t") #'ess-view-data-toggle-maxprint) 289 | (define-key keymap (kbd "C-c C-s") #'ess-view-data-select) 290 | (define-key keymap (kbd "C-c C-u") #'ess-view-data-unselect) 291 | (define-key keymap (kbd "C-c C-f") #'ess-view-data-filter) 292 | (define-key keymap (kbd "C-c C-o") #'ess-view-data-sort) 293 | ;; (define-key keymap (kbd "C-c C-g") #'ess-view-data-group) 294 | ;; (define-key keymap (kbd "C-c C-G") #'ess-view-data-ungroup) 295 | (define-key keymap (kbd "C-c C-i") #'ess-view-data-slice) 296 | (define-key keymap (kbd "C-c C-l") #'ess-view-data-unique) 297 | (define-key keymap (kbd "C-c C-v") #'ess-view-data-summarise) 298 | (define-key keymap (kbd "C-c C-r") #'ess-view-data-reset) 299 | (define-key keymap (kbd "C-c C-w") #'ess-view-data-save) 300 | (define-key keymap (kbd "M-g p") #'ess-view-data-goto-previous-page) 301 | (define-key keymap (kbd "M-g n") #'ess-view-data-goto-next-page) 302 | (define-key keymap (kbd "M-g f") #'ess-view-data-goto-first-page) 303 | (define-key keymap (kbd "M-g l") #'ess-view-data-goto-last-page) 304 | (define-key keymap (kbd "?") #'ess-view-data-transient) 305 | keymap) 306 | "Keymap for function `ess-view-data-mode'.") 307 | 308 | (transient-define-prefix ess-view-data-transient () 309 | "ESS View Data commands." 310 | [["Navigation" 311 | ("n" "Next page" ess-view-data-goto-next-page) 312 | ("p" "Previous page" ess-view-data-goto-previous-page) 313 | ("F" "First page" ess-view-data-goto-first-page) 314 | ("l" "Last page" ess-view-data-goto-last-page) 315 | ("g" "Goto page" ess-view-data-goto-page-number)] 316 | ["View" 317 | ("t" "Toggle maxprint" ess-view-data-toggle-maxprint) 318 | ("P" "Print" ess-view-data-print-ex)] 319 | ["Data Manipulation" 320 | ("s" "Select columns" ess-view-data-select) 321 | ("u" "Unselect columns" ess-view-data-unselect) 322 | ("f" "Filter rows" ess-view-data-filter) 323 | ("o" "Sort" ess-view-data-sort) 324 | ("i" "Slice" ess-view-data-slice) 325 | ("m" "Mutate" ess-view-data-mutate) 326 | ("" "Long to wide (pivot_wider)" ess-view-data-long2wide-pivot-wider) 327 | ("C-" "Wide to long (pivot_longer)" ess-view-data-wide2long-pivot-longer)] 328 | ["Summarize" 329 | ("c" "Count" ess-view-data-count) 330 | ("U" "Unique" ess-view-data-unique) 331 | ("v" "Summarise" ess-view-data-summarise) 332 | ("S" "Skimr" ess-view-data-skimr)] 333 | ["Other" 334 | ("V" "Any data manipulation verb" ess-view-data-verbs) 335 | ("r" "Reset" ess-view-data-reset) 336 | ("w" "Save to csv file" ess-view-data-save) 337 | ("q" "Quit" ess-view-data-quit)]]) 338 | 339 | ;;; Indirect Buffers Minor Mode 340 | (defvar ess-view-data-edit-mode-map 341 | (let ((map (make-sparse-keymap))) 342 | (define-key map "\C-c'" #'ess-view-data-do-commit) 343 | (define-key map "\C-c\C-k" #'ess-view-data-commit-abort) 344 | (define-key map "\C-c\C-i" #'ess-view-data-complete-object) 345 | (define-key map "\C-c\C-l" #'ess-view-data-complete-data) 346 | (define-key map "\C-c\C-a" #'ess-view-data-insert-all-cols) 347 | (define-key map "\C-c\C-v" #'ess-view-data-insert-all-values) 348 | map) 349 | "Keymap for `ess-view-data-edit-mode', a minor mode.") 350 | 351 | (defvar ess-view-data-edit-mode-hook nil 352 | "Hook for the `ess-view-data-edit-mode' minor mode.") 353 | 354 | (define-minor-mode ess-view-data-edit-mode 355 | "Minor mode for special key bindings in a ess-view-data-edit buffer. 356 | 357 | Turning on this mode runs the normal hook `ess-view-data-edit-mode-hook'." 358 | :lighter " Evd" 359 | (setq-local 360 | header-line-format 361 | (substitute-command-keys 362 | "Edit, then exit with `\\[ess-view-data-do-commit] '' or abort with `\\[ess-view-data-commit-abort]'"))) 363 | 364 | ;;; Utils 365 | 366 | ;;; Backend Access API 367 | 368 | (cl-defgeneric ess-view-data--do-print (backend str) 369 | "Benchmark function to do print. 370 | 371 | Argument BACKEND Backend to dispatch, i.e., 372 | the `ess-view-data-current-update-print-backend'. 373 | Argument STR R script to run.") 374 | 375 | (cl-defgeneric ess-view-data--do-update (backend str) 376 | "Do Update. 377 | 378 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 379 | Argument STR R script to run.") 380 | 381 | (cl-defgeneric ess-view-data--do-summarise (backend str) 382 | "Do summarising. 383 | 384 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 385 | Argument STR R script to run.") 386 | 387 | (cl-defgeneric ess-view-data--create-indirect-buffer (backend str) 388 | "Create indirect-buffer for editing. 389 | 390 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 391 | Argument STR R script to run.") 392 | 393 | (cl-defgeneric ess-view-data--do-reset (backend str) 394 | "Reset print buffer. 395 | 396 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 397 | Argument STR R script to run.") 398 | 399 | (cl-defgeneric ess-view-data-do-save (backend str) 400 | "Save. 401 | 402 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 403 | Argument STR R script to run.") 404 | 405 | (cl-defgeneric ess-view-data-do-complete-data (backend str) 406 | "Completing input. 407 | 408 | Argument BACKEND Backend to dispatch, i.e., 409 | the `ess-view-data-current-complete-backend'. 410 | Argument STR R script to run.") 411 | 412 | (cl-defgeneric ess-view-data-get-total-page (backend str) 413 | "Total number of pages. 414 | 415 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 416 | Argument STR R script to run.") 417 | 418 | (cl-defgeneric ess-view-data--header-line (backend str) 419 | "Head-line. 420 | 421 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 422 | Argument STR R script to run.") 423 | 424 | (cl-defgeneric ess-view-data--initialize-backend (_backend) 425 | "Initialization." 426 | nil) 427 | 428 | (cl-defgeneric ess-view-data-do-kill-buffer-hook (backend str) 429 | "Functions to run after `kill-buffer' on '*R Data View' buffer. 430 | 431 | Argument BACKEND Backend to dispatch, i.e., the `ess-view-data-current-backend'. 432 | Argument STR R script to run.") 433 | 434 | ;;; * print-backend: print 435 | (defvar ess-view-data--print-format 436 | (concat 437 | (format 438 | (concat 439 | "op.tmp <- options(\"width\", \"tibble.width\", \"crayon.enabled\");" 440 | "options(tibble.width = Inf, width = %d, crayon.enabled = FALSE);") 441 | ess-view-data-options-width) 442 | "print(%s, n = nrow(%s));" 443 | "options(op.tmp)") 444 | "Format string for print.") 445 | 446 | (defvar ess-view-data--print-format-with-crayon 447 | (concat 448 | (format 449 | (concat 450 | "op.tmp <- options(\"width\", \"tibble.width\", \"crayon.enabled\");" 451 | "options(tibble.width = Inf, width = %d, crayon.enabled = TRUE);") 452 | ess-view-data-options-width) 453 | "print(%s, n = nrow(%s));" 454 | "options(op.tmp)") 455 | "Format string for print, with crayon.enabled for tibble.") 456 | 457 | (cl-defmethod ess-view-data--do-print ((_backend (eql print))) 458 | "Do print using print." 459 | (if ess-view-data-tibble-crayon-enabled-p 460 | ess-view-data--print-format-with-crayon 461 | ess-view-data--print-format)) 462 | 463 | ;;; * kable-backend: kable 464 | (defvar ess-view-data--kable-format 465 | (concat 466 | (format 467 | (concat 468 | "op.tmp <- options(\"width\", \"tibble.width\", \"crayon.enabled\");" 469 | "options(tibble.width = Inf, width = %d, crayon.enabled = FALSE);") 470 | ess-view-data-options-width) 471 | "print(knitr::kable(%s, n = nrow(%s)));" 472 | "options(op.tmp)") 473 | "Format string for kable.") 474 | 475 | (cl-defmethod ess-view-data--do-print ((_backend (eql kable))) 476 | "Do print using kable." 477 | ess-view-data--kable-format) 478 | 479 | 480 | ;;; * backend: dplyr 481 | 482 | ;;; ** Initialization 483 | (cl-defmethod ess-view-data--initialize-backend ((_backend (eql dplyr)) proc-name proc) 484 | "Initialization. 485 | 486 | Initializing the history of operations, make temp object. 487 | 488 | Optional argument PROC-NAME The name of associated ESS process, 489 | usually `ess-local-process-name'. 490 | Optional argument PROC The associated ESS process." 491 | (let ((obj-space-p (string-match-p ess-view-data-objname-regex ess-view-data-object)) 492 | (obj-back-quote-p (string-match-p "`" ess-view-data-object)) 493 | (obj-back-quote (replace-regexp-in-string "`" "" ess-view-data-object))) 494 | (unless ess-view-data-history 495 | (setq ess-view-data-history 496 | (format (cond (obj-back-quote-p "as_tibble(%s)") 497 | (obj-space-p "as_tibble(`%s`)") 498 | (t "as_tibble(%s)")) 499 | ess-view-data-object))) 500 | ;; Initializing the temporary object, for stepwise 501 | (unless ess-view-data-temp-object 502 | (setq ess-view-data-temp-object 503 | (format (cond (obj-back-quote-p "`%s`") 504 | (obj-space-p "`%s`") 505 | (t "`%s`")) 506 | (make-temp-name obj-back-quote))) 507 | (when (and proc-name proc 508 | (not (process-get proc 'busy))) 509 | (ess-command (concat "{suppressPackageStartupMessages(require(dplyr)); " 510 | ;; ess-command using local 2021-12-04 511 | ess-view-data-temp-object " <<- as_tibble(" 512 | (format (cond (obj-back-quote-p "`%s`") 513 | (obj-space-p "`%s`") 514 | (t "`%s`")) 515 | obj-back-quote) 516 | ")}\n") 517 | nil nil nil nil proc)))) 518 | (cl-pushnew ess-view-data-temp-object ess-view-data-temp-object-list) 519 | (delete-dups ess-view-data-temp-object-list)) 520 | 521 | 522 | ;; (defvar csv--header-line) 523 | (defvar-local csv--header-line nil) 524 | (declare-function csv-header-line "csv-mode") 525 | 526 | (cl-defmethod ess-view-data--header-line ((_backend (eql dplyr))) 527 | "Make header-line for dplyr." 528 | (goto-char (point-min)) 529 | ;; (if (looking-at "# A tibble:") 530 | ;; (delete-region (point-min) (1+ (line-end-position)))) 531 | (let ((lin 1)) 532 | (while ;; (looking-at-p "^\\(+\\|#\\)") 533 | (search-forward-regexp "^\\([+]\\|#\\|[[].+?#\\)" nil t) 534 | (forward-line) 535 | (setq lin (1+ lin))) 536 | (unless (fboundp 'csv-header-line) (require 'csv-mode nil t)) 537 | (when (fboundp 'csv-header-line) 538 | (setq csv--header-line nil) 539 | (with-no-warnings (csv-header-line lin)))) 540 | (goto-char (point-min))) 541 | 542 | (cl-defmethod ess-view-data-get-total-page ((_backend (eql dplyr)) proc-name proc) 543 | "Get total number of pages of the current object (data.frame/tibble/data.table). 544 | 545 | If `ess-view-data-maxprint-p' is nil, it will show 100 rows/lines 546 | per page for dplyr+print/kable. 547 | 548 | Optional argument PROC-NAME The name of associated ESS process, 549 | usually `ess-local-process-name'. 550 | Optional argument PROC The associated ESS process." 551 | (when (and proc-name proc 552 | (not (process-get proc 'busy))) 553 | (setq ess-view-data-total-page 554 | (string-to-number 555 | (car (ess-get-words-from-vector 556 | (format "as.character(nrow(%s))\n" ess-view-data-temp-object))))) 557 | (setq ess-view-data-total-page 558 | (1+ (floor (/ ess-view-data-total-page ess-view-data-rows-per-page)))))) 559 | 560 | 561 | 562 | (cl-defmethod ess-view-data-do-kill-buffer-hook ((_backend (eql dplyr)) proc-name proc) 563 | "Functions to run after `kill-buffer' on '*R Data View' buffer. 564 | 565 | The default is to rm the temporary object. 566 | 567 | Optional argument PROC-NAME The name of associated ESS process, 568 | usually `ess-local-process-name'. 569 | Optional argument PROC The associated ESS process." 570 | (when (and proc-name proc 571 | (not (process-get proc 'busy))) 572 | (ess-command (format "rm(%s, envir = globalenv())\n" ess-view-data-temp-object)) 573 | (ess-write-to-dribble-buffer (format "[ESS-v] rm(%s, envir = globalenv())\n" ess-view-data-temp-object)))) 574 | 575 | 576 | ;;; ** Utilities 577 | (cl-defmethod ess-view-data--do-update ((_backend (eql dplyr)) fun action) 578 | "Update the data frame by dplyr stepwisely. 579 | 580 | Optional argument FUN What to do with the data, e.g., 581 | verb like select, filter, and etc.. 582 | Optional argument ACTION Parameter (R script) for FUN, e.g., columns for select." 583 | (let (cmdhist cmd result) 584 | (setq cmdhist 585 | (pcase fun 586 | ('select 587 | (format " %%>%% dplyr::select(%s)" 588 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 589 | ('filter 590 | (format " %%>%% dplyr::filter(%s)" action)) 591 | ('mutate 592 | (format " %%>%% dplyr::mutate(%s)" action)) 593 | ('sort 594 | (format " %%>%% dplyr::arrange(%s, .by_group = TRUE)" 595 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 596 | ('group 597 | (format " %%>%% dplyr::group_by(%s)" 598 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 599 | ('ungroup 600 | (format " %%>%% dplyr::ungroup(%s)" 601 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 602 | ('transmute 603 | (format " %%>%% dplyr::transmute(%s)" action)) 604 | ('wide2long 605 | (format " %%>%% tidyr::gather(%s)" action)) 606 | ('long2wide 607 | (format " %%>%% tidyr::spread(%s)" action)) 608 | ('wide2long-pivot_longer 609 | (format " %%>%% tidyr::pivot_longer(%s)" action)) 610 | ('long2wide-pivot_wider 611 | (format " %%>%% tidyr::pivot_wider(%s)" action)) 612 | ('slice 613 | (format " %%>%% dplyr::slice(%s)" action)) 614 | ('unselect 615 | (format " %%>%% dplyr::select(%s)" 616 | (mapconcat (lambda (x) (concat "-" x)) 617 | (delete-dups (nreverse action)) ","))) 618 | (_ 619 | (format " %%>%% %s" action)))) 620 | 621 | (setq ess-view-data-page-number 0) 622 | (setq cmd (concat 623 | ess-view-data-temp-object " <<- " ess-view-data-temp-object cmdhist "; " 624 | "local({" 625 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 626 | (concat ess-view-data-temp-object 627 | (unless ess-view-data-maxprint-p 628 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 629 | ess-view-data-page-number 630 | ess-view-data-rows-per-page 631 | ess-view-data-temp-object))) 632 | ess-view-data-temp-object) 633 | "})\n")) 634 | (setq result (cons cmdhist cmd)) 635 | result)) 636 | 637 | 638 | (cl-defmethod ess-view-data--do-summarise ((_backend (eql dplyr)) fun action) 639 | "Do summarising by dplyr stepwisely, without modify the data frame. 640 | 641 | Optional argument FUN What to do with the data, e.g., 642 | verb like count, unique, and etc.. 643 | Optional argument ACTION Parameter (R script) for FUN, e.g., columns for count." 644 | (let (cmdhist cmd result) 645 | (setq cmdhist 646 | (pcase fun 647 | ('count 648 | (format " %%>%% dplyr::count(%s)" 649 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 650 | ('unique 651 | (format " %%>%% dplyr::distinct(%s)" 652 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 653 | ('slice 654 | (format " %%>%% dplyr::slice(%s)" action)) 655 | ('skimr 656 | (format " %%>%% skimr::skim(%s)" 657 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 658 | ('skimr-all 659 | " %>% skimr::skim()") 660 | ;; ('summarise 661 | ;; (format " %%>%% dplyr::summarise(%s)" action)) 662 | (_ 663 | (format " %%>%% %s" action)))) 664 | 665 | (setq cmd (concat 666 | "local({" 667 | (format (ess-view-data--do-print ess-view-data-current-summarize-print-backend) 668 | (concat ess-view-data-temp-object cmdhist) 669 | ess-view-data-temp-object) 670 | "})\n")) 671 | (setq result (cons cmdhist cmd)) 672 | result)) 673 | 674 | (cl-defmethod ess-view-data--do-reset ((_backend (eql dplyr)) action) 675 | "Update the data frame by dplyr stepwisely. 676 | 677 | Optional argument ACTION R script to reset the view process, 678 | which will become the cmd history." 679 | (let (cmdhist cmd result) 680 | (setq cmdhist action) 681 | (setq ess-view-data-page-number 0) 682 | (setq cmd (concat 683 | ess-view-data-temp-object " <<- " cmdhist "; " 684 | "local({" 685 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 686 | (concat ess-view-data-temp-object 687 | (unless ess-view-data-maxprint-p 688 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 689 | ess-view-data-page-number 690 | ess-view-data-rows-per-page 691 | ess-view-data-temp-object))) 692 | ess-view-data-temp-object) 693 | "})\n")) 694 | (setq result (cons cmdhist cmd)) 695 | result)) 696 | 697 | (cl-defmethod ess-view-data-do-goto-page ((_backend (eql dplyr)) page &optional pnumber) 698 | "Goto PAGE. 699 | 700 | Optional argument PNUMBER The page number to go to." 701 | (let (cmd result) 702 | (setq ess-view-data-page-number 703 | (pcase page 704 | ('first 0) 705 | ('last ess-view-data-total-page) 706 | ('previous (max 0 (1- ess-view-data-page-number))) 707 | ('next (min (1+ ess-view-data-page-number) ess-view-data-total-page)) 708 | ('page (max (min pnumber ess-view-data-total-page) 0)) 709 | (_ ess-view-data-page-number))) 710 | 711 | (setq cmd (concat 712 | "local({" 713 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 714 | (concat ess-view-data-temp-object 715 | (unless ess-view-data-maxprint-p 716 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 717 | ess-view-data-page-number 718 | ess-view-data-rows-per-page 719 | ess-view-data-temp-object))) 720 | ess-view-data-temp-object) 721 | "})\n")) 722 | (setq result (cons nil cmd)) 723 | result)) 724 | 725 | (defvar-local ess-view-data--parent-buffer nil) 726 | (defvar-local ess-view-data--reset-buffer-p nil) 727 | (defvar-local ess-view-data--action nil) 728 | (defvar-local ess-local-process-name nil) 729 | 730 | (cl-defmethod ess-view-data--create-indirect-buffer 731 | ((_backend (eql dplyr)) 732 | type fun obj-list temp-object parent-buf proc-name) 733 | "Create an edit-indirect buffer and return it. 734 | 735 | Optional argument TYPE Action type, e.g., update, reset, summarise. 736 | Optional argument FUN Action function to do with data, e.g., 737 | select, count, and etc.. 738 | Optional argument OBJ-LIST Columns/variables to do with. 739 | Optional argument TEMP-OBJECT Temporary data in the view process. 740 | Optional argument PARENT-BUF The associated parent buffer for the view process. 741 | Optional argument PROC-NAME The name of associated ESS process, 742 | usually `ess-local-process-name'." 743 | (let ((buf (get-buffer-create (format ess-view-data-source-buffer-name-format temp-object))) 744 | pts) 745 | (with-current-buffer buf 746 | (ess-r-mode) 747 | (set-buffer-modified-p nil) 748 | (setq ess-view-data--parent-buffer parent-buf) 749 | (setq ess-view-data--reset-buffer-p t) 750 | (setq ess-view-data--action `((:type . ,type) (:function . ,fun))) 751 | ;; (print (alist-get :function ess-view-data--action)) 752 | ;; (print (alist-get ':type ess-view-data--action)) 753 | (insert "# Insert [all] variable name[s] (C-c C-i[a]), [all] Values (C-c C-l[v])\n") 754 | (insert "# Line started with `#' will be omitted\n") 755 | (insert "# Don't comment code as all code will be wrapped in one line\n") 756 | (pcase fun 757 | ('filter 758 | (setq ess-view-data-completion-object (car obj-list)) 759 | (insert "# dplyr::filter(...)\n") 760 | (setq pts (point)) 761 | (insert (mapconcat (lambda (x) (propertize x 'evd-object x)) 762 | (delete-dups (nreverse obj-list)) ",")) 763 | (goto-char pts)) 764 | ('mutate 765 | (insert "# dplyr::mutate(...)\n") 766 | (setq pts (point)) 767 | (insert (mapconcat (lambda (x) (format " = %s" (propertize x 'evd-object x))) 768 | (delete-dups (nreverse obj-list)) ",")) 769 | (goto-char pts)) 770 | ('wide2long 771 | (insert "# tidyr::gather(cols, ...)\n") 772 | (insert (format "key = %s, value = %s" (car obj-list) (nth 1 obj-list)))) 773 | ('long2wide 774 | (insert "# tidyr::spread(key to column names)\n") 775 | (insert (format "key = %s, value = %s" (car obj-list) (nth 1 obj-list)))) 776 | ('wide2long-pivot_longer 777 | (insert "# tidyr::pivot_longer(cols, names and values to)\n") 778 | (insert (format "c(), names_to = %s, values_to = %s" (car obj-list) (nth 1 obj-list)))) 779 | ('long2wide-pivot_wider 780 | (insert "# tidyr::pivot_wider(names and values from)\n") 781 | (insert (format "names_from = %s, values_from = %s" (car obj-list) (nth 1 obj-list)))) 782 | ;; ('summarise 783 | ;; (insert "# %> ... \n# Not limited to function summarise\n") 784 | ;; (insert (mapconcat (lambda (x) (format "%s" (propertize x 'evd-object x))) 785 | ;; (delete-dups (nreverse obj-list)) ",")) 786 | ('summarise 787 | (insert "# %> ... \n# Not limited to function summarise\n") 788 | ;; (insert (format "summarise(mean = mean(%s, na.rm = TRUE), n = n())" obj-list)) 789 | (insert "summarise(") 790 | (insert (mapconcat (lambda (x) (format "%s" (propertize x 'evd-object x))) 791 | (delete-dups (nreverse obj-list)) ",")) 792 | (insert ", n = n())")) 793 | ('reset 794 | (insert "# reset\n") 795 | (insert obj-list)) 796 | (_ 797 | (insert "# %> ... \n") 798 | (setq pts (point)) 799 | (insert (mapconcat 'identity (delete-dups (nreverse obj-list)) ",")) 800 | (goto-char pts))) 801 | (setq ess-local-process-name proc-name) 802 | (setq ess-view-data-temp-object 803 | (buffer-local-value 'ess-view-data-temp-object parent-buf)) 804 | (ess-view-data-edit-mode)) 805 | (select-window (display-buffer buf)))) 806 | 807 | 808 | ;;; * backend: dplyr+DT 809 | 810 | 811 | (defcustom ess-view-data-DT-rows-per-page 1000 812 | "Rows per page for DT." 813 | :type 'integer 814 | :group 'ess-view-data) 815 | 816 | (defcustom ess-view-data-cache-directory 817 | (expand-file-name (format "ess-view-data-%d" (user-uid)) 818 | temporary-file-directory) 819 | "The base directory, where the cache files (e.g., html files from DT) 820 | will be saved." 821 | :type 'directory 822 | :group 'ess-view-data) 823 | 824 | 825 | 826 | (defun ess-view-data-make-safe-dir (dir) 827 | "This is from `doc-view-make-safe-dir'. 828 | Just to try create a temporary directory to cache the DT files. 829 | 830 | Argument DIR name of temporary dir." 831 | (condition-case nil 832 | ;; Create temp files with strict access rights. It's easy to 833 | ;; loosen them later, whereas it's impossible to close the 834 | ;; time-window of loose permissions otherwise. 835 | (with-file-modes #o0700 (make-directory dir)) 836 | (file-already-exists 837 | (when (file-symlink-p dir) 838 | (error "Danger: %s points to a symbolic link" dir)) 839 | ;; In case it was created earlier with looser rights. 840 | ;; We could check the mode info returned by file-attributes, but it's 841 | ;; a pain to parse and it may not tell you what we want under 842 | ;; non-standard file-systems. So let's just say what we want and let 843 | ;; the underlying C code and file-system figure it out. 844 | ;; This also ends up checking a bunch of useful conditions: it makes 845 | ;; sure we have write-access to the directory and that we own it, thus 846 | ;; closing a bunch of security holes. 847 | (condition-case error 848 | (set-file-modes dir #o0700) 849 | (file-error 850 | (error 851 | (format "Unable to use temporary directory %s: %s" 852 | dir (mapconcat #'identity (cdr error) " ")))))))) 853 | 854 | 855 | ;;; ** Initialization 856 | (cl-defmethod ess-view-data--initialize-backend ((_backend (eql dplyr+DT)) proc-name proc) 857 | "Initialization. 858 | 859 | Initializing the history of operations, make temp object. 860 | 861 | Optional argument PROC-NAME The name of associated ESS process, 862 | usually `ess-local-process-name'. 863 | Optional argument PROC The associated ESS process." 864 | (let ((obj-space-p (string-match-p ess-view-data-objname-regex ess-view-data-object)) 865 | (obj-back-quote-p (string-match-p "`" ess-view-data-object)) 866 | (obj-back-quote (replace-regexp-in-string "`" "" ess-view-data-object))) 867 | (unless ess-view-data-history 868 | (setq ess-view-data-history 869 | (format (cond (obj-back-quote-p "as_tibble(%s)") 870 | (obj-space-p "as_tibble(`%s`)") 871 | (t "as_tibble(%s)")) 872 | ess-view-data-object))) 873 | ;; Initializing the temporary object, for stepwise 874 | (unless ess-view-data-temp-object 875 | (setq ess-view-data-temp-object 876 | (format (cond (obj-back-quote-p "`%s`") 877 | (obj-space-p "`%s`") 878 | (t "`%s`")) 879 | (make-temp-name obj-back-quote))) 880 | (ess-view-data-make-safe-dir ess-view-data-cache-directory) 881 | (when (and proc-name proc 882 | (not (process-get proc 'busy))) 883 | (ess-command (concat "{suppressPackageStartupMessages(require(dplyr));" 884 | "suppressPackageStartupMessages(require(DT)); " 885 | ess-view-data-temp-object " <<- as_tibble(" 886 | (format (cond (obj-back-quote-p "`%s`") 887 | (obj-space-p "`%s`") 888 | (t "`%s`")) 889 | obj-back-quote) 890 | ")}\n") 891 | nil nil nil nil proc)))) 892 | (cl-pushnew ess-view-data-temp-object ess-view-data-temp-object-list) 893 | (delete-dups ess-view-data-temp-object-list)) 894 | 895 | 896 | (cl-defmethod ess-view-data--header-line ((_backend (eql dplyr+DT))) 897 | "Make header-line for dplyr+DT." 898 | (goto-char (point-min)) 899 | (browse-url (format "%s/%s.html" ess-view-data-cache-directory 900 | (replace-regexp-in-string "`" "" ess-view-data-temp-object)))) 901 | 902 | 903 | (cl-defmethod ess-view-data-get-total-page ((_backend (eql dplyr+DT)) proc-name proc) 904 | "Get the total number of pages. 905 | 906 | Get total number of pages of the current object (data.frame/tibble/data.table). 907 | 908 | If `ess-view-data-maxprint-p' is nil, it will show 1000 rows/lines per page 909 | for DT. 910 | 911 | Optional argument PROC-NAME The name of associated ESS process, 912 | usually `ess-local-process-name'. 913 | Optional argument PROC The associated ESS process." 914 | (when (and proc-name proc 915 | (not (process-get proc 'busy))) 916 | (setq ess-view-data-total-page 917 | (string-to-number 918 | (car (ess-get-words-from-vector 919 | (format "as.character(nrow(%s))\n" ess-view-data-temp-object))))) 920 | (setq ess-view-data-total-page 921 | (1+ (floor (/ ess-view-data-total-page ess-view-data-rows-per-page)))))) 922 | 923 | (cl-defmethod ess-view-data-do-kill-buffer-hook ((_backend (eql dplyr+DT)) proc-name proc) 924 | "Functions to run after `kill-buffer' on '*R Data View' buffer. 925 | 926 | The default is to rm the temporary object. 927 | 928 | Optional argument PROC-NAME The name of associated ESS process, 929 | usually `ess-local-process-name'. 930 | Optional argument PROC The associated ESS process." 931 | (when (and proc-name proc 932 | (not (process-get proc 'busy))) 933 | (ess-command (format "rm(%s, envir = globalenv())\n" ess-view-data-temp-object)) 934 | (ess-write-to-dribble-buffer (format "[ESS-v] rm(%s, envir = globalenv())\n" ess-view-data-temp-object)))) 935 | 936 | ;;; ** Utilities 937 | (cl-defmethod ess-view-data--do-update ((_backend (eql dplyr+DT)) fun action) 938 | "Update the data frame by dplyr stepwisely. 939 | 940 | Optional argument FUN what to do, e.g. select, filter, etc.. 941 | Optional argument ACTION parameters to the FUN." 942 | (let (cmdhist cmd result) 943 | (setq cmdhist 944 | (pcase fun 945 | ('select 946 | (format " %%>%% dplyr::select(%s)" 947 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 948 | ('filter 949 | (format " %%>%% dplyr::filter(%s)" action)) 950 | ('mutate 951 | (format " %%>%% dplyr::mutate(%s)" action)) 952 | ('sort 953 | (format " %%>%% dplyr::arrange(%s, .by_group = TRUE)" 954 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 955 | ('group 956 | (format " %%>%% dplyr::group_by(%s)" 957 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 958 | ('ungroup 959 | (format " %%>%% dplyr::ungroup(%s)" 960 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 961 | ('transmute 962 | (format " %%>%% dplyr::transmute(%s)" action)) 963 | ('wide2long 964 | (format " %%>%% tidyr::gather(%s)" action)) 965 | ('long2wide 966 | (format " %%>%% tidyr::spread(%s)" action)) 967 | ('wide2long-pivot_longer 968 | (format " %%>%% tidyr::pivot_longer(%s)" action)) 969 | ('long2wide-pivot_wider 970 | (format " %%>%% tidyr::pivot_wider(%s)" action)) 971 | ('slice 972 | (format " %%>%% dplyr::slice(%s)" action)) 973 | ('unselect 974 | (format " %%>%% dplyr::select(%s)" 975 | (mapconcat (lambda (x) (concat "-" x)) 976 | (delete-dups (nreverse action)) ","))) 977 | (_ 978 | (format " %%>%% %s" action)))) 979 | 980 | (setq ess-view-data-page-number 0) 981 | (setq cmd (concat 982 | ess-view-data-temp-object " <<- " ess-view-data-temp-object cmdhist "; " 983 | "local({" 984 | (format "DT::saveWidget(datatable(%1$s, filter = 'top' %2$s), file = '%3$s/%4$s.html')\n" 985 | ess-view-data-temp-object 986 | (if ess-view-data-maxprint-p 987 | (format ", options = list(autoWidth = FALSE,pageLength = %d)" 988 | ess-view-data-DT-rows-per-page) 989 | (format ", options = list(lengthMenu = c(10,50,100,%d))" ess-view-data-DT-rows-per-page)) 990 | ess-view-data-cache-directory 991 | (replace-regexp-in-string "`" "" ess-view-data-temp-object)) 992 | "})\n")) 993 | (setq result (cons cmdhist cmd)) 994 | result)) 995 | 996 | 997 | (cl-defmethod ess-view-data--do-summarise ((_backend (eql dplyr+DT)) fun action) 998 | "Do summarising by dplyr stepwisely, without modify the data frame. 999 | 1000 | Optional argument FUN what to do, e.g., count, unique, etc.. 1001 | Optional argument ACTION parameters to the FUN." 1002 | (let (cmdhist cmd result) 1003 | (setq cmdhist 1004 | (pcase fun 1005 | ('count 1006 | (format " %%>%% dplyr::count(%s)" (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1007 | ('unique 1008 | (format " %%>%% dplyr::distinct(%s)" (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1009 | ('slice 1010 | (format " %%>%% dplyr::slice(%s)" action)) 1011 | ('skimr 1012 | (format " %%>%% skimr::skim(%s)" 1013 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1014 | ('skimr-all 1015 | " %>% skimr::skim()") 1016 | ;; ('summarise 1017 | ;; (format " %%>%% dplyr::summarise(%s)" action)) 1018 | (_ 1019 | (format " %%>%% %s" action)))) 1020 | 1021 | (setq cmd (concat 1022 | "local({" 1023 | (format (ess-view-data--do-print ess-view-data-current-summarize-print-backend) 1024 | (concat ess-view-data-temp-object cmdhist) 1025 | ess-view-data-temp-object) 1026 | "})\n")) 1027 | (setq result (cons cmdhist cmd)) 1028 | result)) 1029 | 1030 | (cl-defmethod ess-view-data--do-reset ((_backend (eql dplyr+DT)) action) 1031 | "Update the data frame by dplyr stepwisely. 1032 | 1033 | Optional argument ACTION R script to reset the view process, 1034 | which will become the cmd history." 1035 | (let (cmdhist cmd result) 1036 | (setq cmdhist action) 1037 | (setq ess-view-data-page-number 0) 1038 | (setq cmd (concat 1039 | ess-view-data-temp-object " <<- " cmdhist "; " 1040 | "local({" 1041 | (format "DT::saveWidget(datatable(%1$s, filter = 'top' %2$s), file = '%3$s/%4$s.html')\n" 1042 | ess-view-data-temp-object 1043 | (if ess-view-data-maxprint-p 1044 | (format ", options = list(autoWidth = FALSE,pageLength = %d)" 1045 | ess-view-data-DT-rows-per-page) 1046 | (format ", options = list(lengthMenu = c(10,50,100,%d))" 1047 | ess-view-data-DT-rows-per-page)) 1048 | ess-view-data-cache-directory 1049 | (replace-regexp-in-string "`" "" ess-view-data-temp-object)) 1050 | "})\n")) 1051 | (setq result (cons cmdhist cmd)) 1052 | result)) 1053 | 1054 | (cl-defmethod ess-view-data--create-indirect-buffer 1055 | ((_backend (eql dplyr+DT)) 1056 | type fun obj-list temp-object parent-buf proc-name) 1057 | "Create an edit-indirect buffer and return it. 1058 | 1059 | Optional argument TYPE Action type, e.g., update, reset, summarise. 1060 | Optional argument FUN Action function to do with data, e.g., 1061 | select, count, and etc.. 1062 | Optional argument OBJ-LIST Columns/variables to do with. 1063 | Optional argument TEMP-OBJECT Temporary data in the view process. 1064 | Optional argument PARENT-BUF The associated parent buffer for the view process. 1065 | Optional argument PROC-NAME The name of associated ESS process, 1066 | usually `ess-local-process-name'." 1067 | (let ((buf (get-buffer-create (format ess-view-data-source-buffer-name-format temp-object))) 1068 | pts) 1069 | (with-current-buffer buf 1070 | (ess-r-mode) 1071 | (set-buffer-modified-p nil) 1072 | (setq ess-view-data--parent-buffer parent-buf) 1073 | (setq ess-view-data--reset-buffer-p t) 1074 | (setq ess-view-data--action `((:type . ,type) (:function . ,fun))) 1075 | ;; (print (alist-get :function ess-view-data--action)) 1076 | ;; (print (alist-get ':type ess-view-data--action)) 1077 | (insert "# Insert variable name[s] (C-c i[I]), Insert Values (C-c l[L])\n") 1078 | (insert "# Line started with `#' will be omitted\n") 1079 | (insert "# Don't comment code as all code will be wrapped in one line\n") 1080 | (pcase fun 1081 | ('filter 1082 | (setq ess-view-data-completion-object (car obj-list)) 1083 | (insert "# dplyr::filter(...)\n") 1084 | (setq pts (point)) 1085 | (insert (mapconcat (lambda (x) (propertize x 'evd-object x)) 1086 | (delete-dups (nreverse obj-list)) ",")) 1087 | (goto-char pts)) 1088 | ('mutate 1089 | (insert "# dplyr::mutate(...)\n") 1090 | (setq pts (point)) 1091 | (insert (mapconcat (lambda (x) (format " = %s" (propertize x 'evd-object x))) 1092 | (delete-dups (nreverse obj-list)) ",")) 1093 | (goto-char pts)) 1094 | ('wide2long-gather 1095 | (insert "# tidyr::gather(cols, ...)\n") 1096 | (insert (format "key = %s, value = %s" (car obj-list) (nth 1 obj-list)))) 1097 | ('long2wide-spread 1098 | (insert "# tidyr::spread(key to column names)\n") 1099 | (insert (format "key = %s, value = %s" (car obj-list) (nth 1 obj-list)))) 1100 | ('wide2long-pivot_longer 1101 | (insert "# tidyr::pivot_longer(cols, names and values to)\n") 1102 | (insert (format "c(), names_to = %s, values_to = %s" (car obj-list) (nth 1 obj-list)))) 1103 | ('long2wide-pivot_wider 1104 | (insert "# tidyr::pivot_wider(names and values from)\n") 1105 | (insert (format "names_from = %s, values_from = %s" (car obj-list) (nth 1 obj-list)))) 1106 | ('summarise 1107 | (insert "# %> ... \n# Not limited to function summarise\n") 1108 | ;; (insert (format "summarise(mean = mean(%s, na.rm = TRUE), n = n())" obj-list)) 1109 | (insert "summarise(") 1110 | (insert (mapconcat (lambda (x) (format "%s" (propertize x 'evd-object x))) 1111 | (delete-dups (nreverse obj-list)) ",")) 1112 | (insert ", n = n())")) 1113 | ('reset 1114 | (insert "# reset\n") 1115 | (insert obj-list)) 1116 | (_ 1117 | (insert "# %> ... \n") 1118 | (setq pts (point)) 1119 | (insert (mapconcat 'identity (delete-dups (nreverse obj-list)) ",")) 1120 | (goto-char pts))) 1121 | (setq ess-local-process-name proc-name) 1122 | (setq ess-view-data-temp-object 1123 | (buffer-local-value 'ess-view-data-temp-object parent-buf)) 1124 | (ess-view-data-edit-mode)) 1125 | (select-window (display-buffer buf)))) 1126 | 1127 | 1128 | (cl-defmethod ess-view-data-do-goto-page ((_backend (eql dplyr+DT)) page &optional pnumber) 1129 | "Goto PAGE. Just reset `ess-view-data-page-number' when backend is dplyr+DT. 1130 | 1131 | Optional argument PNUMBER The page number to go to." 1132 | (let (result) 1133 | (setq ess-view-data-page-number 1134 | (pcase page 1135 | ('first 0) 1136 | ('last ess-view-data-total-page) 1137 | ('previous (max 0 (1- ess-view-data-page-number))) 1138 | ('next (min (1+ ess-view-data-page-number) ess-view-data-total-page)) 1139 | ('page (max (min pnumber ess-view-data-total-page) 0)) 1140 | (_ ess-view-data-page-number))) 1141 | 1142 | (setq result (cons nil nil)) 1143 | result)) 1144 | 1145 | 1146 | ;;; * backend: data.table 1147 | 1148 | ;;; ** Initialization 1149 | (cl-defmethod ess-view-data--initialize-backend ((_backend (eql data.table+magrittr)) proc-name proc) 1150 | "Initializing the history of operations. 1151 | 1152 | Optional argument PROC-NAME The name of associated ESS process, 1153 | usually `ess-local-process-name'. 1154 | Optional argument PROC The associated ESS process." 1155 | (let ((obj-space-p (string-match-p ess-view-data-objname-regex ess-view-data-object)) 1156 | (obj-back-quote-p (string-match-p "`" ess-view-data-object)) 1157 | (obj-back-quote (replace-regexp-in-string "`" "" ess-view-data-object))) 1158 | (unless ess-view-data-history 1159 | (setq ess-view-data-history 1160 | (format (cond (obj-back-quote-p "as.data.table(%s)") 1161 | (obj-space-p "as.data.table(`%s`)") 1162 | (t "as.data.table(%s)")) 1163 | ess-view-data-object))) 1164 | ;; Initializing the temporary object, for stepwise 1165 | (unless ess-view-data-temp-object 1166 | (setq ess-view-data-temp-object 1167 | (format (cond (obj-back-quote-p "`%s`") 1168 | (obj-space-p "`%s`") 1169 | (t "`%s`")) 1170 | (make-temp-name obj-back-quote))) 1171 | (when (and proc-name proc 1172 | (not (process-get proc 'busy))) 1173 | (ess-command (concat "{suppressPackageStartupMessages(require(magrittr));" 1174 | "suppressPackageStartupMessages(require(data.table)); " 1175 | ess-view-data-temp-object " <<- as.data.table(" 1176 | (format (cond (obj-back-quote-p "`%s`") 1177 | (obj-space-p "`%s`") 1178 | (t "`%s`")) 1179 | obj-back-quote) 1180 | ")}\n") 1181 | nil nil nil nil proc)))) 1182 | (cl-pushnew ess-view-data-temp-object ess-view-data-temp-object-list) 1183 | (delete-dups ess-view-data-temp-object-list)) 1184 | 1185 | 1186 | (cl-defmethod ess-view-data--header-line ((_backend (eql data.table+magrittr))) 1187 | "Make header-line for data.table+magrittr." 1188 | (goto-char (point-min)) 1189 | (let ((lin 1)) 1190 | (while ;; (looking-at-p "^\\(+\\|#\\)") 1191 | (search-forward-regexp "^\\([+]\\|#\\)" nil t) 1192 | (forward-line) 1193 | (setq lin (1+ lin))) 1194 | (unless (fboundp 'csv-header-line) (require 'csv-mode nil t)) 1195 | (when (fboundp 'csv-header-line) 1196 | (setq csv--header-line nil) 1197 | (with-no-warnings (csv-header-line lin)))) 1198 | (goto-char (point-min))) 1199 | 1200 | (cl-defmethod ess-view-data-get-total-page ((_backend (eql data.table+magrittr)) proc-name proc) 1201 | "Initializing the history of operations. 1202 | 1203 | Optional argument PROC-NAME The name of associated ESS process, 1204 | usually `ess-local-process-name'. 1205 | Optional argument PROC The associated ESS process." 1206 | (when (and proc-name proc 1207 | (not (process-get proc 'busy))) 1208 | (setq ess-view-data-total-page 1209 | (string-to-number 1210 | (car (ess-get-words-from-vector 1211 | (format "as.character(nrow(%s))\n" ess-view-data-temp-object))))) 1212 | (setq ess-view-data-total-page 1213 | (1+ (floor (/ ess-view-data-total-page ess-view-data-rows-per-page)))))) 1214 | 1215 | 1216 | 1217 | (cl-defmethod ess-view-data-do-kill-buffer-hook ((_backend (eql data.table+magrittr)) proc-name proc) 1218 | "Initializing the history of operations. 1219 | 1220 | Optional argument PROC-NAME The name of associated ESS process, 1221 | usually `ess-local-process-name'. 1222 | Optional argument PROC The associated ESS process." 1223 | (when (and proc-name proc 1224 | (not (process-get proc 'busy))) 1225 | (ess-command (format "rm(%s, envir = globalenv())\n" ess-view-data-temp-object)) 1226 | (ess-write-to-dribble-buffer (format "[ESS-v] rm(%s, envir = globalenv())\n" ess-view-data-temp-object)))) 1227 | 1228 | 1229 | ;;; ** Utilities 1230 | (defvar-local ess-view-data--group nil) 1231 | 1232 | (cl-defmethod ess-view-data--do-update ((_backend (eql data.table+magrittr)) fun action) 1233 | "Update the data frame by data.table stepwisely. 1234 | 1235 | Optional argument FUN What to do with the data, e.g., 1236 | verb like select, filter, and etc.. 1237 | Optional argument ACTION Parameter (R script) for FUN, e.g., columns for select." 1238 | (let (cmdhist cmd result) 1239 | (setq cmdhist 1240 | (pcase fun 1241 | ('select 1242 | (format " %%>%% .[, .(%s)]" 1243 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1244 | ('filter 1245 | (format " %%>%% .[%s,]" action)) 1246 | ('mutate 1247 | (format " %%>%% .[,`:=`(%s)]" action)) 1248 | ('sort 1249 | (format " %%>%% setorder(., %s)" 1250 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1251 | ('group 1252 | ;; (error "No single group step for data.table+magrittr") 1253 | (setq ess-view-data--group 1254 | (mapconcat 'identity (delete-dups (nreverse action)) ",")) 1255 | nil) 1256 | ('ungroup 1257 | (error "No single ungroup step for data.table+magrittr")) 1258 | ('transmute 1259 | (format " %%>%% .[,`:=`(%s)]" action)) 1260 | ('wide2long 1261 | (format " %%>%% melt(., %s)" action)) 1262 | ('long2wide 1263 | (format " %%>%% dcast(., %s)" action)) 1264 | ('slice 1265 | (if ess-view-data--group 1266 | (format " %%>%% .[, .SD[%s], by = .(%s)]" action 1267 | ess-view-data--group) 1268 | (error "Group is required for data.table+magrittr"))) 1269 | ('unselect 1270 | (format " %%>%% .[,`:=`(%s)]" 1271 | (mapconcat (lambda (x) (concat x " = NULL")) 1272 | (delete-dups (nreverse action)) ","))) 1273 | (_ 1274 | (format " %%>%% %s" action)))) 1275 | 1276 | (setq ess-view-data-page-number 0) 1277 | (setq cmd (concat 1278 | ess-view-data-temp-object " <<- " ess-view-data-temp-object cmdhist "; " 1279 | "local({" 1280 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 1281 | (concat ess-view-data-temp-object 1282 | (unless ess-view-data-maxprint-p 1283 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 1284 | ess-view-data-page-number 1285 | ess-view-data-rows-per-page 1286 | ess-view-data-temp-object))) 1287 | ess-view-data-temp-object) 1288 | "})\n")) 1289 | (setq result (cons cmdhist cmd)) 1290 | result)) 1291 | 1292 | 1293 | (cl-defmethod ess-view-data--do-summarise ((_backend (eql data.table+magrittr)) fun action) 1294 | "Do summarising by data.table stepwisely, without modify the data frame. 1295 | 1296 | Optional argument FUN What to do with the data, e.g., 1297 | verb like count, unique, and etc.. 1298 | Optional argument ACTION Parameter (R script) for FUN, e.g., columns for count." 1299 | (let (cmdhist cmd result) 1300 | (setq cmdhist 1301 | (pcase fun 1302 | ('count 1303 | (format " %%>%% .[, .N, by = .(%s)] " 1304 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1305 | ('unique 1306 | (format " %%>%% unique(., by = c(\"%s\"))" 1307 | (replace-regexp-in-string 1308 | "^`\\(.*\\)`$" "\\1" 1309 | (mapconcat 'identity (delete-dups (nreverse action)) "\",\"")))) 1310 | ('slice 1311 | (if ess-view-data--group 1312 | (format " %%>%% .[, .SD[%s], by = .(%s)]" action 1313 | ess-view-data--group) 1314 | (error "Group is required for data.table+magrittr"))) 1315 | ('skimr 1316 | (format " %%>%% skimr::skim(%s)" 1317 | (mapconcat 'identity (delete-dups (nreverse action)) ","))) 1318 | ('skimr-all 1319 | " %>% skimr::skim()") 1320 | ('summarise 1321 | (format " %%>%% %s" action)) 1322 | (_ 1323 | (format " %%>%% %s" action)))) 1324 | 1325 | (setq cmd (concat 1326 | "local({" 1327 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 1328 | (concat ess-view-data-temp-object cmdhist) 1329 | ess-view-data-temp-object) 1330 | "})\n")) 1331 | (setq result (cons cmdhist cmd)) 1332 | result)) 1333 | 1334 | (cl-defmethod ess-view-data--do-reset ((_backend (eql data.table+magrittr)) action) 1335 | "Update the data frame by data.table stepwisely. 1336 | 1337 | Optional argument ACTION R script to reset the view process, 1338 | which will become the cmd history." 1339 | (let (cmdhist cmd result) 1340 | (setq cmdhist action) 1341 | (setq ess-view-data-page-number 0) 1342 | (setq cmd (concat 1343 | ess-view-data-temp-object " <<- " cmdhist "; " 1344 | "local({" 1345 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 1346 | (concat ess-view-data-temp-object 1347 | (unless ess-view-data-maxprint-p 1348 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 1349 | ess-view-data-page-number 1350 | ess-view-data-rows-per-page 1351 | ess-view-data-temp-object))) 1352 | ess-view-data-temp-object) 1353 | "})\n")) 1354 | (setq result (cons cmdhist cmd)) 1355 | result)) 1356 | 1357 | (cl-defmethod ess-view-data-do-goto-page ((_backend (eql data.table+magrittr)) page &optional pnumber) 1358 | "Goto PAGE. 1359 | 1360 | Optional argument PNUMBER The page number to go to." 1361 | (let (cmd result) 1362 | (setq ess-view-data-page-number 1363 | (pcase page 1364 | ('first 0) 1365 | ('last ess-view-data-total-page) 1366 | ('previous (max 0 (1- ess-view-data-page-number))) 1367 | ('next (min (1+ ess-view-data-page-number) ess-view-data-total-page)) 1368 | ('page (max (min pnumber ess-view-data-total-page) 0)) 1369 | (_ ess-view-data-page-number))) 1370 | 1371 | (setq cmd (concat 1372 | "local({" 1373 | (format (ess-view-data--do-print ess-view-data-current-update-print-backend) 1374 | (concat ess-view-data-temp-object 1375 | (unless ess-view-data-maxprint-p 1376 | (format "[(%1$d*%2$d + 1) : min((%1$d + 1)*%2$d, nrow(%s)),]" 1377 | ess-view-data-page-number 1378 | ess-view-data-rows-per-page 1379 | ess-view-data-temp-object))) 1380 | ess-view-data-temp-object) 1381 | "})\n")) 1382 | (setq result (cons nil cmd)) 1383 | result)) 1384 | 1385 | (cl-defmethod ess-view-data--create-indirect-buffer 1386 | ((_backend (eql data.table+magrittr)) 1387 | type fun obj-list temp-object parent-buf proc-name) 1388 | "Create an edit-indirect buffer and return it. 1389 | 1390 | Optional argument TYPE Action type, e.g., update, reset, summarise. 1391 | Optional argument FUN Action function to do with data, e.g., 1392 | select, count, and etc.. 1393 | Optional argument OBJ-LIST Columns/variables to do with. 1394 | Optional argument TEMP-OBJECT Temporary data in the view process. 1395 | Optional argument PARENT-BUF The associated parent buffer for the view process. 1396 | Optional argument PROC-NAME The name of associated ESS process, 1397 | usually `ess-local-process-name'." 1398 | (let ((buf (get-buffer-create (format ess-view-data-source-buffer-name-format temp-object))) 1399 | pts) 1400 | (with-current-buffer buf 1401 | (ess-r-mode) 1402 | (set-buffer-modified-p nil) 1403 | (setq ess-view-data--parent-buffer parent-buf) 1404 | (setq ess-view-data--reset-buffer-p t) 1405 | (setq ess-view-data--action `((:type . ,type) (:function . ,fun))) 1406 | ;; (print (alist-get :function ess-view-data--action)) 1407 | ;; (print (alist-get ':type ess-view-data--action)) 1408 | (insert "# Insert variable name[s] (C-c i[I]), Insert Values (C-c l[L])\n") 1409 | (insert "# Line started with `#' will be omitted\n") 1410 | (insert "# Don't comment code as all code will be wrapped in one line\n") 1411 | (pcase fun 1412 | ('filter 1413 | (setq ess-view-data-completion-object (car obj-list)) 1414 | (insert "# DT[...,]\n") 1415 | (setq pts (point)) 1416 | (insert (mapconcat (lambda (x) (propertize x 'evd-object x)) 1417 | (delete-dups (nreverse obj-list)) "&")) 1418 | (goto-char pts)) 1419 | ('mutate 1420 | (insert "# DT[,`:=`(%s)]\n") 1421 | (setq pts (point)) 1422 | (insert (mapconcat (lambda (x) (format " = %s" (propertize x 'evd-object x))) 1423 | (delete-dups (nreverse obj-list)) ",")) 1424 | (goto-char pts)) 1425 | ('wide2long 1426 | (insert "# melt(DT, ...)\n") 1427 | (insert (format "id.vars = c(\"%s\"), measure = col to fill, variable.name = , value.name = c(\"%s\")" 1428 | (car obj-list) (nth 1 obj-list)))) 1429 | ('long2wide 1430 | (insert "# dcast(DT, ...)\n") 1431 | (insert (format "id? ~ %s, value.var = c(\"%s\")" (car obj-list) 1432 | (nth 1 obj-list)))) 1433 | ('summarise 1434 | (insert "# DT[...] \n# Not limited to function summarise\n") 1435 | ;; (insert (format "summarise(mean = mean(%s, na.rm = TRUE), n = n())" obj-list)) 1436 | (insert ".[, .( ), by = .(") 1437 | (insert (mapconcat (lambda (x) (format "%s" (propertize x 'evd-object x))) 1438 | (delete-dups (nreverse obj-list)) ",")) 1439 | (insert ")]")) 1440 | ('reset 1441 | (insert "# reset\n") 1442 | (insert obj-list)) 1443 | (_ 1444 | (insert "# ... \n") 1445 | (setq pts (point)) 1446 | (insert (mapconcat 'identity (delete-dups (nreverse obj-list)) ",")) 1447 | (goto-char pts))) 1448 | (setq ess-local-process-name proc-name) 1449 | (setq ess-view-data-temp-object 1450 | (buffer-local-value 'ess-view-data-temp-object parent-buf)) 1451 | (ess-view-data-edit-mode)) 1452 | (select-window (display-buffer buf)))) 1453 | 1454 | 1455 | 1456 | ;;; * save data 1457 | 1458 | (cl-defmethod ess-view-data-do-save ((_backend (eql write.csv)) file-name) 1459 | "Ess view data doing select by write.csv stepwise. 1460 | 1461 | Optional argument FILE-NAME file name." 1462 | (let (cmd result) 1463 | (setq cmd (concat 1464 | "write.csv(" ess-view-data-temp-object ", file = \"" 1465 | file-name 1466 | "\")\n")) 1467 | (setq result (cons nil cmd)) 1468 | result)) 1469 | 1470 | (cl-defmethod ess-view-data-do-save ((_backend (eql readr::write_csv)) file-name) 1471 | "Ess view data doing select by readr::write_csv stepwise. 1472 | 1473 | Optional argument FILE-NAME file-name." 1474 | (let (cmd result) 1475 | (setq cmd (concat 1476 | "readr::write_csv(" ess-view-data-temp-object ", file = \"" 1477 | file-name 1478 | "\")\n")) 1479 | (setq result (cons nil cmd)) 1480 | result)) 1481 | 1482 | (cl-defmethod ess-view-data-do-save ((_backend (eql data.table::fwrite)) file-name) 1483 | "Ess view data doing select by data.table::fwrite stepwise. 1484 | 1485 | Optional argument FILE-NAME file-name." 1486 | (let (cmd result) 1487 | (setq cmd (concat 1488 | "data.table::fwrite(" ess-view-data-temp-object ", file = \"" 1489 | file-name 1490 | "\")\n")) 1491 | (setq result (cons nil cmd)) 1492 | result)) 1493 | 1494 | 1495 | ;;; * For completion 1496 | (cl-defmethod ess-view-data-do-complete-data ((_backend (eql jsonlite)) &optional dataframe) 1497 | "To get the list for completing in data frame. 1498 | 1499 | Optional argument DATAFRAME dataframe to do complete which will 1500 | be dumped vis toJSON." 1501 | (let (cmd result) 1502 | (setq cmd 1503 | (concat 1504 | "jsonlite::toJSON(" 1505 | (format "c(list(%1$s = names(%1$s)), lapply(%1$s, function(x) as.character(unique(x))))" 1506 | (or dataframe ess-view-data-temp-object)) 1507 | ")\n")) 1508 | (setq result (json-read-from-string (ess-string-command cmd))) 1509 | result)) 1510 | 1511 | 1512 | 1513 | (defun ess-view-data--previous-complete-object (prop) 1514 | "Search for the object. 1515 | 1516 | Argument PROP text property to get the object for completion." 1517 | (let (prop-value) 1518 | (while (progn 1519 | (goto-char (previous-single-char-property-change (point) prop)) 1520 | (not (or (setq prop-value (get-text-property (point) prop)) 1521 | (eobp) 1522 | (bobp))))) 1523 | prop-value)) 1524 | 1525 | 1526 | 1527 | (defun ess-view-data-complete-data (&optional arg) 1528 | "Ess view data do complete. 1529 | 1530 | Optional argument ARG if non-nil, it will read the which variable 1531 | to be completed." 1532 | (interactive "P") 1533 | (unless (and ;; (string= "R" ess-dialect) 1534 | ess-local-process-name) 1535 | (error "Not in an R buffer with attached process")) 1536 | (let* ((buf (current-buffer)) 1537 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 1538 | (proc (get-process proc-name))) 1539 | ;; Initializing backed 1540 | ;; (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 1541 | 1542 | (unless ess-view-data-completion-candidate 1543 | (when (and proc-name proc 1544 | (not (process-get proc 'busy))) 1545 | (setq ess-view-data-completion-candidate 1546 | (ess-view-data-do-complete-data ess-view-data-current-complete-backend))))) 1547 | 1548 | (let (evd-object) 1549 | 1550 | (if (or arg (null (save-excursion 1551 | (save-restriction 1552 | (setq evd-object (ess-view-data--previous-complete-object 'evd-object)))))) 1553 | (progn 1554 | (let* ((possible-completions (ess-r-get-rcompletions)) 1555 | (token-string (or (car possible-completions) "")) 1556 | (start (- (point) (length token-string))) 1557 | (end (point))) 1558 | (setq evd-object 1559 | (funcall ess-view-data-read-string 1560 | "Variable: " 1561 | (delq nil (delete-dups (append 1562 | (if (assq (intern ess-view-data-temp-object) 1563 | ess-view-data-completion-candidate) 1564 | (alist-get (intern ess-view-data-temp-object) 1565 | ess-view-data-completion-candidate) 1566 | (alist-get (intern (replace-regexp-in-string 1567 | "`" "" ess-view-data-temp-object)) 1568 | ess-view-data-completion-candidate)) 1569 | nil))) 1570 | nil nil token-string)) 1571 | (delete-region start end) 1572 | ;; propertize 1573 | (insert (propertize evd-object 'evd-object evd-object)))) 1574 | 1575 | (if evd-object 1576 | (let* ((possible-completions (ess-r-get-rcompletions)) 1577 | (token-string (or (car possible-completions) "")) 1578 | (start (- (point) (length token-string))) 1579 | (end (point)) 1580 | com) 1581 | (setq com 1582 | (funcall ess-view-data-read-string 1583 | "Value: " 1584 | (delq nil (delete-dups (append 1585 | (if (assq (intern evd-object) 1586 | ess-view-data-completion-candidate) 1587 | (alist-get (intern evd-object) 1588 | ess-view-data-completion-candidate) 1589 | (alist-get (intern (replace-regexp-in-string 1590 | "`" "" evd-object)) 1591 | ess-view-data-completion-candidate)) 1592 | nil))) 1593 | nil nil token-string)) 1594 | (delete-region start end) 1595 | (insert com)))))) 1596 | 1597 | 1598 | (defun ess-view-data-insert-all-cols () 1599 | "Insert all column/variable names." 1600 | (interactive) 1601 | (unless (and ;; (string= "R" ess-dialect) 1602 | ess-local-process-name) 1603 | (error "Not in an R buffer with attached process")) 1604 | (let* ((buf (current-buffer)) 1605 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 1606 | (proc (get-process proc-name))) 1607 | ;; Initializing backed 1608 | ;; (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 1609 | 1610 | (unless ess-view-data-completion-candidate 1611 | (when (and proc-name proc 1612 | (not (process-get proc 'busy))) 1613 | (setq ess-view-data-completion-candidate 1614 | (ess-view-data-do-complete-data ess-view-data-current-complete-backend))))) 1615 | 1616 | (if ess-view-data-completion-candidate 1617 | (let* ((obj-list (append 1618 | (if (assq (intern ess-view-data-temp-object) 1619 | ess-view-data-completion-candidate) 1620 | (alist-get (intern ess-view-data-temp-object) 1621 | ess-view-data-completion-candidate) 1622 | (alist-get (intern (replace-regexp-in-string 1623 | "`" "" ess-view-data-temp-object)) 1624 | ess-view-data-completion-candidate)) 1625 | nil))) 1626 | (insert (mapconcat (lambda (x) (propertize x 'evd-object x)) 1627 | (delete-dups obj-list) ","))))) 1628 | 1629 | 1630 | (defun ess-view-data-insert-all-values () 1631 | "Insert all column/variable names." 1632 | (interactive) 1633 | (unless (and ;; (string= "R" ess-dialect) 1634 | ess-local-process-name) 1635 | (error "Not in an R buffer with attached process")) 1636 | (let* ((buf (current-buffer)) 1637 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 1638 | (proc (get-process proc-name))) 1639 | ;; Initializing backed 1640 | ;; (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 1641 | 1642 | (unless ess-view-data-completion-candidate 1643 | (when (and proc-name proc 1644 | (not (process-get proc 'busy))) 1645 | (setq ess-view-data-completion-candidate 1646 | (ess-view-data-do-complete-data ess-view-data-current-complete-backend))))) 1647 | 1648 | (let (evd-object) 1649 | (save-excursion 1650 | (save-restriction 1651 | (setq evd-object (ess-view-data--previous-complete-object 'evd-object)))) 1652 | 1653 | (if evd-object 1654 | (let* ((obj-list (append 1655 | (if (assq (intern evd-object) 1656 | ess-view-data-completion-candidate) 1657 | (alist-get (intern evd-object) 1658 | ess-view-data-completion-candidate) 1659 | (alist-get (intern (replace-regexp-in-string 1660 | "`" "" evd-object)) 1661 | ess-view-data-completion-candidate)) 1662 | nil))) 1663 | (insert (format "\"%s\""(mapconcat 'identity (delete-dups obj-list) ","))))))) 1664 | 1665 | 1666 | (defun ess-view-data-complete-object () 1667 | "Ess view data do complete object name." 1668 | (interactive) 1669 | (ess-view-data-complete-data 1)) 1670 | 1671 | (defun ess-view-data-complete-set-object () 1672 | "Set object for completion." 1673 | (interactive) 1674 | (unless (and ;; (string= "R" ess-dialect) 1675 | ess-local-process-name) 1676 | (error "Not in an R buffer with attached process")) 1677 | (let* ((buf (current-buffer)) 1678 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 1679 | (proc (get-process proc-name))) 1680 | ;; Initializing backed 1681 | ;; (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 1682 | 1683 | (unless ess-view-data-completion-candidate 1684 | (when (and proc-name proc 1685 | (not (process-get proc 'busy))) 1686 | (setq ess-view-data-completion-candidate 1687 | (ess-view-data-do-complete-data ess-view-data-current-complete-backend))))) 1688 | 1689 | (let* ((possible-completions (ess-r-get-rcompletions)) 1690 | (token-string (or (car possible-completions) "")) 1691 | object) 1692 | (setq object 1693 | (funcall ess-view-data-read-string 1694 | "Variable: " 1695 | (append 1696 | (if (assq (intern ess-view-data-temp-object) 1697 | ess-view-data-completion-candidate) 1698 | (alist-get (intern ess-view-data-temp-object) 1699 | ess-view-data-completion-candidate) 1700 | (alist-get (intern (replace-regexp-in-string 1701 | "`" "" ess-view-data-temp-object)) 1702 | ess-view-data-completion-candidate)) 1703 | nil) 1704 | nil nil token-string)) 1705 | (insert (propertize " " 'evd-object object)))) 1706 | 1707 | ;;; * Export function 1708 | 1709 | (defun ess-view-data-do-commit () 1710 | "Commit the modifications done in an edit-indirect buffer. 1711 | 1712 | Can be called only when the current buffer is an edit-indirect buffer." 1713 | (interactive) 1714 | (let* ((parent-buffer ess-view-data--parent-buffer) 1715 | (proc-name (buffer-local-value 'ess-local-process-name parent-buffer)) 1716 | (proc (get-process proc-name)) 1717 | (fill-column most-positive-fixnum) 1718 | (fun (alist-get :function ess-view-data--action)) 1719 | (type (alist-get :type ess-view-data--action)) 1720 | command) 1721 | (with-current-buffer (current-buffer) 1722 | (when ess-view-data--reset-buffer-p 1723 | (save-excursion 1724 | (save-match-data 1725 | (goto-char (point-min)) 1726 | (flush-lines "^#") 1727 | (fill-region (point-min) (point-max)) 1728 | (setq command (buffer-substring-no-properties (point-min) (point-max))) 1729 | ;; make command in one line to avoid the print of ` + ' in the output buffer 1730 | (setq command (replace-regexp-in-string "\n+" " " command)))) 1731 | (kill-buffer))) 1732 | 1733 | (pop-to-buffer parent-buffer) 1734 | 1735 | (when (and proc-name proc command 1736 | (not (process-get proc 'busy))) 1737 | (setq command 1738 | (pcase type 1739 | ('update 1740 | (ess-view-data--do-update ess-view-data-current-backend fun command)) 1741 | ('summarise 1742 | (ess-view-data--do-summarise ess-view-data-current-backend fun command)) 1743 | ('reset 1744 | (ess-view-data--do-reset ess-view-data-current-backend command)))) 1745 | (ess-command (concat "{" (cdr command) "}") parent-buffer nil nil nil proc) 1746 | (ess-write-to-dribble-buffer (format "[ESS-v] %s.\n" (symbol-name fun))) 1747 | (with-current-buffer parent-buffer 1748 | ;; (ansi-color-apply-on-region (point-min) (point-max)) 1749 | (when (memq type '(update reset)) 1750 | (if (eql type 'reset) 1751 | (setq ess-view-data-history (car command)) 1752 | (setq ess-view-data-history (concat ess-view-data-history (car command)))) 1753 | (setq ess-view-data-page-number 0) 1754 | (ess-view-data-get-total-page ess-view-data-current-backend proc-name proc)) 1755 | (ess-write-to-dribble-buffer (format "# Trace: %s\n" ess-view-data-history)) 1756 | (ess-write-to-dribble-buffer (format "# Last: %s\n" (car command))) 1757 | (goto-char (point-min)) 1758 | ;; (toggle-truncate-lines 1) 1759 | ;; (setq-local scroll-preserve-screen-position t) 1760 | (when ess-view-data-show-code 1761 | (insert (format "# Trace: %s\n" ess-view-data-history)) 1762 | (insert (format "# Last: %s\n" (car command)))) 1763 | (when (memq type '(update reset)) 1764 | (unless (or ess-view-data-maxprint-p ess-view-data-show-no-page-number) 1765 | (insert (format "# Page number: %d / %d\n" 1766 | (1+ ess-view-data-page-number) ess-view-data-total-page)))) 1767 | ;; (delete-line) 1768 | (goto-char (point-min)) 1769 | (ess-view-data-mode 1) 1770 | (goto-char (point-min)) 1771 | (ess-view-data--header-line ess-view-data-current-backend))))) 1772 | 1773 | 1774 | (defun ess-view-data-do-apply (type fun indirect &optional desc trans prompt) 1775 | "Update data frame. 1776 | 1777 | Argument TYPE Action type, e.g., update, reset, summarise. 1778 | Argument FUN Action function to do with data, e.g., select, count, etc.. 1779 | Argument INDIRECT Indirect buffer to edit the parameters or verbs. 1780 | Optional argument DESC if non-nil, then descending. 1781 | Optional argument TRANS if non-nil, read key and value for transform. 1782 | Optional argument PROMPT prompt for `read-string'." 1783 | (unless (and ;; (string= "R" ess-dialect) 1784 | ess-local-process-name) 1785 | (error "Not in an R buffer with attached process")) 1786 | (let* ((buf (current-buffer)) 1787 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 1788 | (proc (get-process proc-name)) 1789 | (obj " ") 1790 | obj-list 1791 | objs 1792 | objs2 1793 | command) 1794 | ;; Initializing backed 1795 | (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 1796 | ;; variables 1797 | (if (eql 'reset fun) 1798 | ;; reset 1799 | (ess-view-data--create-indirect-buffer ess-view-data-current-backend 1800 | type fun 1801 | ess-view-data-history 1802 | ess-view-data-temp-object buf proc-name) 1803 | ;; other actions 1804 | (when (and proc-name proc 1805 | (not (process-get proc 'busy))) 1806 | (if prompt 1807 | ;; general read-string 1808 | (setq obj-list (read-string prompt)) 1809 | ;; read column names 1810 | (setq objs (ess-get-words-from-vector 1811 | (concat "colnames(" ess-view-data-temp-object ")\n"))) ;; or "ls()" 1812 | ;; In case the colname is not simple 1813 | (setq objs (mapcar (lambda (x) 1814 | (format (if (string-match-p ess-view-data-objname-regex x) 1815 | "`%s`" "%s") 1816 | x)) 1817 | objs)) 1818 | (if desc 1819 | (setq objs (apply 'append 1820 | `(,objs 1821 | ,(mapcar (lambda (x) (format desc x)) objs))))) 1822 | (if trans 1823 | (progn 1824 | (setq obj (funcall ess-view-data-read-string "key" objs)) 1825 | (setq objs2 (funcall ess-view-data-read-string "value" objs)) 1826 | (setq obj-list (list obj objs2))) 1827 | (if (equal ess-view-data-read-string 'completing-read) 1828 | (setq obj-list (nreverse (completing-read-multiple "Select Variables: " objs))) 1829 | (while (not (equal obj "")) 1830 | (setq obj (funcall ess-view-data-read-string 1831 | (format "Variable (%s), C-j to finish" 1832 | (mapconcat 'identity 1833 | (setq objs2 (nreverse objs2)) 1834 | ",")) 1835 | objs)) 1836 | (unless (equal obj "") 1837 | (setq objs (delete obj objs)) 1838 | (cl-pushnew obj obj-list) 1839 | (cl-pushnew obj objs2)))))) 1840 | (if indirect 1841 | (when obj-list 1842 | (ess-view-data--create-indirect-buffer ess-view-data-current-backend 1843 | type fun obj-list 1844 | ess-view-data-temp-object 1845 | buf proc-name)) 1846 | (when obj-list 1847 | (setq command 1848 | (pcase type 1849 | ('update 1850 | (ess-view-data--do-update ess-view-data-current-backend fun obj-list)) 1851 | ('summarise 1852 | (ess-view-data--do-summarise ess-view-data-current-backend fun obj-list))))) 1853 | (when (and proc-name proc command 1854 | (not (process-get proc 'busy))) 1855 | (ess-command (concat "{" (cdr command) "}") buf nil nil nil proc) 1856 | (ess-write-to-dribble-buffer (format "[ESS-v] %s.\n" (symbol-name fun))) 1857 | (with-current-buffer buf 1858 | ;; (ansi-color-apply-on-region (point-min) (point-max)) 1859 | (when (eql type 'update) 1860 | (setq ess-view-data-history (concat ess-view-data-history (car command))) 1861 | (setq ess-view-data-page-number 0) 1862 | (ess-view-data-get-total-page ess-view-data-current-backend proc-name proc)) 1863 | (ess-write-to-dribble-buffer (format "# Trace: %s\n" ess-view-data-history)) 1864 | (ess-write-to-dribble-buffer (format "# Last: %s\n" (car command))) 1865 | (goto-char (point-min)) 1866 | (when ess-view-data-show-code 1867 | (insert (format "# Trace: %s\n" ess-view-data-history)) 1868 | (insert (format "# Last: %s\n" (car command)))) 1869 | (when (eql type 'update) 1870 | (unless (or ess-view-data-maxprint-p ess-view-data-show-no-page-number) 1871 | (insert (format "# Page number: %d / %d\n" 1872 | (1+ ess-view-data-page-number) ess-view-data-total-page)))) 1873 | ;; (delete-line) 1874 | (goto-char (point-min)) 1875 | (ess-view-data-mode 1) 1876 | (goto-char (point-min)) 1877 | (ess-view-data--header-line ess-view-data-current-backend)))))))) 1878 | 1879 | 1880 | 1881 | 1882 | (defun ess-view-data-select () 1883 | "Select columns/variables." 1884 | (interactive) 1885 | (ess-view-data-do-apply 'update 'select nil nil)) 1886 | 1887 | (defun ess-view-data-unselect () 1888 | "Select columns/variables." 1889 | (interactive) 1890 | (ess-view-data-do-apply 'update 'unselect nil nil)) 1891 | 1892 | (defun ess-view-data-sort () 1893 | "Sort columns/variables." 1894 | (interactive) 1895 | (ess-view-data-do-apply 1896 | 'update 'sort nil 1897 | (plist-get (alist-get ess-view-data-current-backend ess-view-data-backend-setting) :desc))) 1898 | 1899 | (defun ess-view-data-group () 1900 | "Group columns/variables." 1901 | (interactive) 1902 | (ess-view-data-do-apply 'update 'group nil nil)) 1903 | 1904 | (defun ess-view-data-ungroup () 1905 | "Ungroup columns/variables." 1906 | (interactive) 1907 | (ess-view-data-do-apply 'update 'ungroup nil nil)) 1908 | 1909 | 1910 | ;; filter 1911 | (defun ess-view-data-filter () 1912 | "Do filter." 1913 | (interactive) 1914 | (ess-view-data-do-apply 'update 'filter t nil)) 1915 | 1916 | ;; mutate 1917 | (defun ess-view-data-mutate () 1918 | "Do mutate." 1919 | (interactive) 1920 | (ess-view-data-do-apply 'update 'mutate t nil)) 1921 | 1922 | (defun ess-view-data-slice () 1923 | "Slice." 1924 | (interactive) 1925 | (ess-view-data-do-apply 1926 | 'update 'slice nil nil nil 1927 | (plist-get (alist-get ess-view-data-current-backend ess-view-data-backend-setting) :slice))) 1928 | 1929 | 1930 | ;; wide2long 1931 | (defun ess-view-data-wide2long () 1932 | "Do wide2long." 1933 | (interactive) 1934 | (ess-view-data-do-apply 'update 'wide2long t nil t)) 1935 | 1936 | ;; wide2long 1937 | (defun ess-view-data-wide2long-pivot-longer () 1938 | "Do wide2long using 'pivot_longer'." 1939 | (interactive) 1940 | (ess-view-data-do-apply 'update 'wide2long-pivot_longer t nil t)) 1941 | 1942 | ;; long2wide 1943 | (defun ess-view-data-long2wide () 1944 | "Do long2wide." 1945 | (interactive) 1946 | (ess-view-data-do-apply 'update 'long2wide t nil t)) 1947 | 1948 | ;; long2wide - Pivot_wider 1949 | (defun ess-view-data-long2wide-pivot-wider () 1950 | "Do long2wide using 'pivot_wider'." 1951 | (interactive) 1952 | (ess-view-data-do-apply 'update 'long2wide-pivot_wider t nil t)) 1953 | 1954 | ;; update 1955 | (defun ess-view-data-update () 1956 | "Do update." 1957 | (interactive) 1958 | (ess-view-data-do-apply 'update 'update t nil)) 1959 | 1960 | ;;; ** reset 1961 | (defun ess-view-data-reset () 1962 | "Do filter." 1963 | (interactive) 1964 | (ess-view-data-do-apply 'reset 'reset t nil)) 1965 | 1966 | 1967 | ;;; ** summarise 1968 | (defun ess-view-data-unique () 1969 | "Unique." 1970 | (interactive) 1971 | (ess-view-data-do-apply 'summarise 'unique nil nil)) 1972 | 1973 | 1974 | (defun ess-view-data-count () 1975 | "Count." 1976 | (interactive) 1977 | (ess-view-data-do-apply 'summarise 'count nil nil)) 1978 | 1979 | (defun ess-view-data-skimr () 1980 | "Count." 1981 | (interactive) 1982 | (ess-view-data-do-apply 'summarise 'skimr nil nil)) 1983 | 1984 | (defun ess-view-data-summarise () 1985 | "Ess view data do summarise." 1986 | (interactive) 1987 | (ess-view-data-do-apply 'summarise 'summarise t nil)) 1988 | 1989 | (defun ess-view-data-overview () 1990 | "Ess view data do summarise." 1991 | (interactive) 1992 | (ess-view-data-do-apply 'summarise 'overview t nil)) 1993 | 1994 | 1995 | (defun ess-view-data-verbs (verb) 1996 | "Select the VERB to do." 1997 | (interactive (list (completing-read 1998 | "verb: " 1999 | (append ess-view-data-verb-update-list 2000 | ess-view-data-verb-update-indirect-list 2001 | ess-view-data-verb-summarise-list 2002 | ess-view-data-verb-summarise-indirect-list 2003 | '("reset")) 2004 | nil t))) 2005 | (cond 2006 | ((member verb ess-view-data-verb-update-list) 2007 | (ess-view-data-do-apply 'update (intern verb) nil nil)) 2008 | ((member verb ess-view-data-verb-update-indirect-list) 2009 | (ess-view-data-do-apply 'update (intern verb) t nil)) 2010 | ((member verb ess-view-data-verb-summarise-list) 2011 | (ess-view-data-do-apply 'summarise (intern verb) nil nil)) 2012 | ((member verb ess-view-data-verb-summarise-indirect-list) 2013 | (ess-view-data-do-apply 'summarise (intern verb) t nil)) 2014 | ((string= verb "reset") 2015 | (ess-view-data-do-apply 'reset 'reset t nil)))) 2016 | 2017 | 2018 | 2019 | (defun ess-view-data-commit-abort () 2020 | "Kill the edit-indirect buffer." 2021 | (interactive) 2022 | (kill-buffer)) 2023 | 2024 | 2025 | 2026 | ;; scroll data 2027 | 2028 | ;;; ** goto page 2029 | (defun ess-view-data-goto-page (page &optional pnumber) 2030 | "Goto PAGE. 2031 | Optional argument PNUMBER page number to go." 2032 | (unless (and ;; (string= "R" ess-dialect) 2033 | ess-local-process-name) 2034 | (error "Not in an R buffer with attached process")) 2035 | (let* ((buf (current-buffer)) 2036 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 2037 | (proc (get-process proc-name)) 2038 | command) 2039 | ;; Initializing backed 2040 | (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 2041 | 2042 | (setq command 2043 | (ess-view-data-do-goto-page ess-view-data-current-backend page pnumber)) 2044 | 2045 | (when (and proc-name proc command 2046 | (not (process-get proc 'busy))) 2047 | (ess-command (concat "{" (cdr command) "}") buf nil nil nil proc) 2048 | (with-current-buffer buf 2049 | (goto-char (point-min)) 2050 | ;; (ansi-color-apply-on-region (point-min) (point-max)) 2051 | ;; (toggle-truncate-lines 1) 2052 | ;; (setq-local scroll-preserve-screen-position t) 2053 | (when ess-view-data-show-code 2054 | (insert (format "# Trace: %s\n" ess-view-data-history))) 2055 | (when ess-view-data-show-no-page-number 2056 | (insert (format "# Page number: %d / %d\n" 2057 | (1+ ess-view-data-page-number) 2058 | ess-view-data-total-page))) 2059 | ;; (delete-line) 2060 | (goto-char (point-min)) 2061 | (ess-view-data-mode 1) 2062 | (goto-char (point-min)) 2063 | (ess-view-data--header-line ess-view-data-current-backend))))) 2064 | 2065 | 2066 | (defun ess-view-data-goto-next-page () 2067 | "Ess view data do select." 2068 | (interactive) 2069 | (ess-view-data-goto-page 'next)) 2070 | 2071 | (defun ess-view-data-goto-previous-page () 2072 | "Ess view data do select." 2073 | (interactive) 2074 | (ess-view-data-goto-page 'previous)) 2075 | 2076 | (defun ess-view-data-goto-first-page () 2077 | "Ess view data do select." 2078 | (interactive) 2079 | (ess-view-data-goto-page 'first)) 2080 | 2081 | (defun ess-view-data-goto-last-page () 2082 | "Ess view data do select." 2083 | (interactive) 2084 | (ess-view-data-goto-page 'last)) 2085 | 2086 | 2087 | (defun ess-view-data-goto-page-number (&optional pnumber) 2088 | "Ess view data do select. 2089 | 2090 | Optional argument PNUMBER The page number to go to." 2091 | (interactive "NGoto page:") 2092 | ;; (unless pnumber ) 2093 | (ess-view-data-goto-page 'page (1- pnumber))) 2094 | 2095 | 2096 | ;; save 2097 | (defun ess-view-data-save () 2098 | "Ess view data do save." 2099 | (interactive) 2100 | (unless (and ;; (string= "R" ess-dialect) 2101 | ess-local-process-name) 2102 | (error "Not in an R buffer with attached process")) 2103 | (let* ((buf (current-buffer)) 2104 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 2105 | (proc (get-process proc-name)) 2106 | file-name 2107 | command) 2108 | ;; Initializing backed 2109 | (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 2110 | ;; slice variables 2111 | (setq file-name (find-file-read-args "Find file: " 2112 | (confirm-nonexistent-file-or-buffer))) 2113 | (if file-name 2114 | (setq command 2115 | (ess-view-data-do-save ess-view-data-current-save-backend (car file-name)))) 2116 | (when (and proc-name proc command 2117 | (not (process-get proc 'busy))) 2118 | (ess-command (concat "{" (cdr command) "}") nil nil nil nil proc) 2119 | (ess-write-to-dribble-buffer "[ESS-v] Saved.\n") 2120 | (ess-write-to-dribble-buffer (format "# Trace: %s\n" ess-view-data-history)) 2121 | (ess-write-to-dribble-buffer (format "# Last: %s\n" (car command))) 2122 | (with-current-buffer buf 2123 | (goto-char (point-min)))))) 2124 | 2125 | 2126 | 2127 | ;; utilities 2128 | (defun ess-view-data-quit () 2129 | "Quit from ess-view-data." 2130 | (interactive) 2131 | (kill-buffer)) 2132 | 2133 | (defun ess-view-data-kill-buffer-hook () 2134 | "Hook for `kill-buffer' to clean environment." 2135 | (let* ((proc-name (buffer-local-value 'ess-local-process-name (current-buffer))) 2136 | (proc (get-process proc-name))) 2137 | (ess-view-data-do-kill-buffer-hook ess-view-data-current-backend proc-name proc))) 2138 | 2139 | 2140 | (define-minor-mode ess-view-data-mode 2141 | "ess-view-data" 2142 | :global nil 2143 | :group 'ess-view-data 2144 | :keymap ess-view-data-mode-map 2145 | :lighter " ESS-V" 2146 | (if ess-view-data-mode 2147 | (progn 2148 | (require 'ansi-color) 2149 | (ansi-color-apply-on-region (point-min) (point-max)) 2150 | (setq buffer-read-only t) 2151 | (setq mode-line-process 2152 | '(" [" 2153 | (:eval (format "%d/%d" 2154 | ess-view-data-page-number 2155 | ess-view-data-total-page)) 2156 | "]")) 2157 | (force-mode-line-update) 2158 | (add-hook 'kill-buffer-hook #'ess-view-data-kill-buffer-hook nil t) 2159 | (when ess-view-data-auto-show-transient 2160 | (ess-view-data-transient))))) 2161 | 2162 | (defun ess-view-data-print-ex (&optional obj proc-name maxprint) 2163 | "Do print. 2164 | 2165 | Optional argument OBJ the object (data.frame/tibble etc.) to print and view. 2166 | Optional argument PROC-NAME the name of associated ESS process. 2167 | Optional argument MAXPRINT if non-nil, 100 rows/lines per page; if t, show all." 2168 | (interactive "P") 2169 | (let* ((obj (or obj ess-view-data-object)) 2170 | (proc-name (or proc-name (buffer-local-value 'ess-local-process-name (current-buffer)))) 2171 | (buf (get-buffer-create (format ess-view-data-buffer-name-format obj proc-name))) 2172 | ;; (proc-name-buf (buffer-local-value 'ess-local-process-name buf)) 2173 | (proc (get-process proc-name)) 2174 | command) 2175 | ;; (if (or (not proc-name-buf) (equal proc-name proc-name-buf)) 2176 | ;; A new view or from the same process 2177 | (with-current-buffer buf 2178 | (if maxprint 2179 | (setq ess-view-data-maxprint-p (not ess-view-data-maxprint-p))) 2180 | (unless ess-view-data-object 2181 | (setq ess-view-data-object obj) 2182 | (setq ess-local-process-name proc-name)) 2183 | (ess-view-data--initialize-backend ess-view-data-current-backend proc-name proc) 2184 | (ess-view-data-get-total-page ess-view-data-current-backend proc-name proc) 2185 | (setq command 2186 | (ess-view-data--do-reset ess-view-data-current-backend 2187 | (format "%s" ess-view-data-temp-object)))) 2188 | 2189 | (when (and proc-name proc 2190 | (not (process-get proc 'busy))) 2191 | (ess-command (concat "{" (cdr command) "}") buf nil nil nil proc) 2192 | ;; (ansi-color-apply-on-region (point-min) (point-max)) 2193 | (ess-write-to-dribble-buffer "[ESS-v] Print.\n") 2194 | (ess-write-to-dribble-buffer (format "# Trace: %s\n" ess-view-data-history)) 2195 | (with-current-buffer buf 2196 | (setq-local scroll-preserve-screen-position t) 2197 | (toggle-truncate-lines 1) 2198 | (goto-char (point-min)) 2199 | (when ess-view-data-show-code 2200 | (insert (format "# Trace: %s\n" ess-view-data-history))) 2201 | (unless (or ess-view-data-maxprint-p ess-view-data-show-no-page-number) 2202 | (insert (format "# Page number: %d / %d\n" 2203 | (1+ ess-view-data-page-number) 2204 | ess-view-data-total-page))) 2205 | ;; (delete-line) 2206 | (goto-char (point-min)) 2207 | (ess-view-data--header-line ess-view-data-current-backend) 2208 | (ess-view-data-mode 1)) 2209 | buf))) 2210 | 2211 | 2212 | 2213 | ;;;###autoload 2214 | (defun ess-view-data-print (&optional maxprint) 2215 | "Ess R dv using pprint. 2216 | Optional argument MAXPRINT maxprint." 2217 | (interactive "P") 2218 | (unless (and ;; (string= "R" ess-dialect) 2219 | ess-local-process-name) 2220 | (error "Not in an R buffer with attached process")) 2221 | (let* ((obj (or ess-view-data-object 2222 | (tabulated-list-get-id) 2223 | ;; (current-word) 2224 | (funcall ess-view-data-read-string 2225 | "Object: " 2226 | (ess-get-words-from-vector "Filter(function(x) inherits(get(x, envir = .GlobalEnv), 'data.frame'), ls(envir = .GlobalEnv))\n") 2227 | nil nil (current-word))))) 2228 | (pop-to-buffer (ess-view-data-print-ex obj maxprint)))) 2229 | 2230 | 2231 | (defun ess-view-data-clean-up () 2232 | "Ess view data do select." 2233 | (interactive) 2234 | (unless (and ;; (string= "R" ess-dialect) 2235 | ess-local-process-name) 2236 | (error "Not in an R buffer with attached process")) 2237 | (let* ((buf (current-buffer)) 2238 | (proc-name (buffer-local-value 'ess-local-process-name buf)) 2239 | (proc (get-process proc-name)) 2240 | command) 2241 | (setq command (concat "rm(" 2242 | (mapconcat 'identity ess-view-data-temp-object-list 2243 | ",") 2244 | ", envir = globalenv())\n")) 2245 | (when (and proc-name proc command 2246 | (not (process-get proc 'busy))) 2247 | (ess-command (concat "{" command "}") nil nil nil nil proc)) 2248 | (setq ess-view-data-temp-object-list '(ess-view-data-temp-object)))) 2249 | 2250 | 2251 | (defun ess-view-data-toggle-maxprint () 2252 | "Ess view data do select." 2253 | (interactive) 2254 | (setq ess-view-data-page-number 0) 2255 | (setq ess-view-data-maxprint-p (not ess-view-data-maxprint-p))) 2256 | 2257 | (defun ess-view-data-make-header-line () 2258 | "Ess view data do select." 2259 | (interactive) 2260 | (ess-view-data--header-line ess-view-data-current-backend)) 2261 | 2262 | 2263 | (defun ess-view-data-set-backend (manipulate update summarise write complete) 2264 | "Set backend. 2265 | 2266 | Argument MANIPULATE `ess-view-data-current-backend' from 2267 | `ess-view-data-backend-list'. 2268 | Argument UPDATE `ess-view-data-current-update-print-backend' from 2269 | `ess-view-data-print-backend-list'. 2270 | Argument SUMMARISE `ess-view-data-current-summarize-print-backend' from 2271 | `ess-view-data-print-backend-list'. 2272 | Argument WRITE `ess-view-data-current-save-backend' from 2273 | `ess-view-data-save-backend-list'. 2274 | Argument COMPLETE `ess-view-data-current-complete-backend' from 2275 | `ess-view-data-complete-backend-list'." 2276 | (interactive (list (completing-read 2277 | (format "Backend for data manipulate (%s): " 2278 | ess-view-data-current-backend) 2279 | (mapcar (lambda (x) 2280 | (symbol-name x)) 2281 | ess-view-data-backend-list) 2282 | nil t) 2283 | (completing-read 2284 | (format "Backend for data print in Emacs buffer (%s): " 2285 | ess-view-data-current-update-print-backend) 2286 | (mapcar (lambda (x) 2287 | (symbol-name x)) 2288 | ess-view-data-print-backend-list) 2289 | nil t) 2290 | (completing-read 2291 | (format "Backend for summary print in Emacs buffer (%s): " 2292 | ess-view-data-current-summarize-print-backend) 2293 | (mapcar (lambda (x) 2294 | (symbol-name x)) 2295 | ess-view-data-print-backend-list) 2296 | nil t) 2297 | (completing-read 2298 | (format "Backend for save data (%s): " 2299 | ess-view-data-current-save-backend) 2300 | (mapcar (lambda (x) 2301 | (symbol-name x)) 2302 | ess-view-data-save-backend-list) 2303 | nil t) 2304 | (completing-read 2305 | (format "Backend for completion (%s): " 2306 | ess-view-data-current-complete-backend) 2307 | (mapcar (lambda (x) 2308 | (symbol-name x)) 2309 | ess-view-data-complete-backend-list) 2310 | nil t))) 2311 | (unless (or (null manipulate) (string-blank-p manipulate)) 2312 | (setq ess-view-data-current-backend (intern manipulate))) 2313 | (unless (or (null update) (string-blank-p update)) 2314 | (setq ess-view-data-current-update-print-backend (intern update))) 2315 | (unless (or (null summarise) (string-blank-p summarise)) 2316 | (setq ess-view-data-current-summarize-print-backend (intern summarise))) 2317 | (unless (or (null write) (string-blank-p write)) 2318 | (setq ess-view-data-current-save-backend (intern write))) 2319 | (unless (or (null complete) (string-blank-p complete)) 2320 | (setq ess-view-data-current-complete-backend (intern complete)))) 2321 | 2322 | 2323 | 2324 | (provide 'ess-view-data) 2325 | ;;; ess-view-data.el ends here 2326 | -------------------------------------------------------------------------------- /screenshot/ess-view-data-count.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-count.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-filter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-filter.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-log.png -------------------------------------------------------------------------------- /screenshot/ess-view-data-overview-skimr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-overview-skimr.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-print.png -------------------------------------------------------------------------------- /screenshot/ess-view-data-reset.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-reset.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-select.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-sort.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-sort.gif -------------------------------------------------------------------------------- /screenshot/ess-view-data-summarise.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShuguangSun/ess-view-data/5ec1c7206f1431c7b24f0990497ecc7e0fb33939/screenshot/ess-view-data-summarise.gif --------------------------------------------------------------------------------