├── main.rkt ├── scribblings ├── .gitignore ├── sauron.scrbl ├── develop.scrbl └── user-guide.scrbl ├── req.json ├── .gitignore ├── cmd ├── raco.rkt └── execute.rkt ├── .hooks └── pre-commit ├── tool ├── bind-key.rkt ├── repl.rkt ├── editor.rkt └── frame.rkt ├── project ├── dir-state.rkt ├── project-templates.rkt ├── manager.rkt └── panel.rkt ├── collect ├── record.rkt ├── record-maintainer.rkt ├── api.rkt └── collector.rkt ├── jump-to-def.rkt ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── test.yml │ └── coverage.yml ├── path ├── util.rkt ├── ignore.rkt └── renamer.rkt ├── version-control ├── parse-git.rkt ├── pusher.rkt └── panel.rkt ├── info.rkt ├── log.rkt ├── LICENSE-MIT ├── README.md ├── CONTRIBUTING.md ├── meta.rkt ├── repl └── history.rkt ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── shortcut.rkt └── LICENSE-APACHE /main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | -------------------------------------------------------------------------------- /scribblings/.gitignore: -------------------------------------------------------------------------------- 1 | sauron/ 2 | -------------------------------------------------------------------------------- /req.json: -------------------------------------------------------------------------------- 1 | { 2 | "local": [ 3 | [".", "sauron"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#* 3 | .\#* 4 | .DS_Store 5 | compiled/ 6 | /doc/ 7 | coverage/ 8 | -------------------------------------------------------------------------------- /scribblings/sauron.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | 3 | @title{sauron} 4 | @author{dannypsnl} 5 | 6 | Sauron works as a DrRacket plugin to provide everything an IDE shall have! 7 | 8 | @include-section{user-guide.scrbl} 9 | @include-section{develop.scrbl} 10 | -------------------------------------------------------------------------------- /cmd/raco.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide raco) 4 | 5 | (require raco/all-tools) 6 | 7 | (define (raco command . args) 8 | (define raco-make-spec (hash-ref (all-tools) command)) 9 | (parameterize ([current-command-line-arguments (list->vector args)]) 10 | (dynamic-require (second raco-make-spec) #f))) 11 | -------------------------------------------------------------------------------- /.hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g') 3 | [ -z "$FILES" ] && exit 0 4 | 5 | # Format all selected files 6 | echo "$FILES" | egrep '\.rkt$' | xargs raco fmt -i 7 | 8 | # Add back the modified/prettified files to staging 9 | echo "$FILES" | xargs git add 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /tool/bind-key.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (provide tool@) 3 | (require drracket/tool 4 | framework 5 | racket/runtime-path) 6 | 7 | (define-runtime-path file "../shortcut.rkt") 8 | 9 | (define-unit tool@ 10 | (import drracket:tool^) 11 | (export drracket:tool-exports^) 12 | (define (phase1) 13 | (preferences:set 'framework:auto-set-wrap? #f)) 14 | (define (phase2) 15 | (void)) 16 | (keymap:add-user-keybindings-file file)) 17 | -------------------------------------------------------------------------------- /scribblings/develop.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | @(require (for-label sauron 3 | racket 4 | racket/gui 5 | framework/preferences)) 6 | 7 | @title[#:tag "develop"]{Develop} 8 | 9 | ctrl/command and alt/option just rely on this function from racket/gui @code{get-default-shortcut-prefix}. 10 | 11 | @section{information in preferences} 12 | 13 | @itemlist[ 14 | @item{You can get @code{(preferences:get 'current-project)} to know current editing project, default value is @code{#f}} 15 | ] 16 | -------------------------------------------------------------------------------- /project/dir-state.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide dir-open? 4 | open-dir 5 | close-dir) 6 | 7 | (define dir-state (make-hash)) 8 | (define (dir-open? dir) 9 | (hash-ref dir-state dir #f)) 10 | (define (open-dir dir) 11 | (hash-set! dir-state dir #t)) 12 | (define (close-dir dir) 13 | (hash-set! dir-state dir #f)) 14 | 15 | (module+ test 16 | (require rackunit) 17 | 18 | (check-equal? (dir-open? "test") #f) 19 | (open-dir "test") 20 | (check-equal? (dir-open? "test") #t) 21 | (close-dir "test") 22 | (check-equal? (dir-open? "test") #f)) 23 | -------------------------------------------------------------------------------- /collect/record.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide make-record 3 | (struct-out record)) 4 | (require data/interval-map) 5 | 6 | (define (make-record #:created-time [created-time (current-seconds)] 7 | #:doc [doc (make-interval-map)] 8 | #:defs [defs (make-interval-map)] 9 | #:requires [requires (make-hash)]) 10 | (record created-time 11 | doc 12 | defs 13 | requires)) 14 | 15 | (struct record 16 | (created-time 17 | doc 18 | defs 19 | requires) 20 | #:transparent) 21 | -------------------------------------------------------------------------------- /jump-to-def.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (provide (struct-out jump-pos) 3 | jump-add! 4 | jump-pop!) 5 | (require "log.rkt") 6 | 7 | ;;; Jump stack management 8 | (struct jump-pos (tab pos) #:transparent) 9 | 10 | (define (jump-add! tab pos) 11 | (log:info "jump add pos: ~a:~a" (send (send tab get-defs) get-filename) pos) 12 | (set! jump-stack (cons (jump-pos tab pos) jump-stack))) 13 | (define (jump-pop!) 14 | (if (empty? jump-stack) 15 | #f 16 | (match-let ([(cons p rest) jump-stack]) 17 | (set! jump-stack rest) 18 | p))) 19 | 20 | (define jump-stack '()) 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /project/project-templates.rkt: -------------------------------------------------------------------------------- 1 | #lang curly-fn racket/base 2 | (provide project-templates) 3 | 4 | (require json 5 | net/url 6 | racket/port 7 | try-catch-finally) 8 | 9 | (define project-templates 10 | (try 11 | (map #{hash-ref %1 'name} 12 | (call/input-url 13 | (string->url "https://api.github.com/repos/racket-templates/racket-templates/contents/templates") 14 | get-pure-port 15 | (compose string->jsexpr port->string))) 16 | (catch _ 17 | '("cli-command" "gui-app" "lang" "package" "ppict-slideshow" "qi-tutorial" "raco-command" "rosette" "template" "web-app")))) 18 | 19 | (module+ test 20 | (require rackunit) 21 | 22 | (check-pred list? project-templates)) 23 | -------------------------------------------------------------------------------- /path/util.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide basename 3 | basepath 4 | config-dir 5 | parent-path) 6 | 7 | (define config-dir (build-path (find-system-path 'home-dir) ".sauron")) 8 | ; create config directory if not exised 9 | (unless (directory-exists? config-dir) 10 | (make-directory config-dir)) 11 | 12 | (define (basepath path) 13 | (define-values (base file dir?) (split-path path)) 14 | file) 15 | 16 | (define (basename path) 17 | (path->string (basepath path))) 18 | 19 | (define (parent-path path) 20 | (define-values [base file dir?] (split-path path)) 21 | base) 22 | 23 | (module+ test 24 | (require rackunit 25 | racket/path 26 | racket/runtime-path) 27 | 28 | (define-runtime-path this-dir ".") 29 | 30 | (check-equal? (basename (normalize-path this-dir)) "path")) 31 | -------------------------------------------------------------------------------- /cmd/execute.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide run) 4 | 5 | (require racket/match 6 | racket/system 7 | framework/preferences) 8 | 9 | (define (run cmd [callback #f] [dir (preferences:get 'current-project)]) 10 | (parameterize ([current-directory dir]) 11 | (match-let ([(list out in pid err invoke) (process cmd)]) 12 | (invoke 'wait) 13 | 14 | (when callback 15 | (callback out in err)) 16 | 17 | (close-output-port in) 18 | (close-input-port out) 19 | (close-input-port err)))) 20 | 21 | (module+ test 22 | (require rackunit) 23 | (define test-layer (preferences:new-layer (preferences:current-layer))) 24 | (parameterize ([preferences:current-layer test-layer]) 25 | (preferences:set-default 'current-project (current-directory) path-string?) 26 | 27 | (check-equal? (run "ls") (void)))) 28 | -------------------------------------------------------------------------------- /version-control/parse-git.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide parse-git-output) 4 | 5 | (require racket/string) 6 | 7 | (define (parse-git-output output) 8 | (values 9 | (cond 10 | [(ormap (λ (x) (string-prefix? output x)) 11 | '("M " "D " "A ")) 12 | 'ready] 13 | [(ormap (λ (x) (string-prefix? output x)) 14 | '(" M " " D " "AM " "MM " "UU " "?? ")) 15 | 'changes] 16 | [else (error 'unknown-format output)]) 17 | (substring output 3))) 18 | 19 | (module+ test 20 | (require rackunit) 21 | 22 | (test-case "parse git: ready" 23 | (define-values (ty _) 24 | (parse-git-output "M ")) 25 | (check-equal? ty 'ready)) 26 | (test-case "parse git: changes" 27 | (define-values (ty _) 28 | (parse-git-output " M ")) 29 | (check-equal? ty 'changes))) 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[bug]" 5 | labels: bug 6 | assignees: dannypsnl 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots(or error message)** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. macOS, linux, windows] 28 | - Sauron version: [e.g. 1.3.1] 29 | - Racket version: [e.g. 8.5] 30 | 31 | **Additional context(or any ideas)** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /version-control/pusher.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | (provide make-pusher) 3 | 4 | (require sauron/cmd/execute) 5 | 6 | (define (make-pusher command) 7 | (run "git fetch") 8 | (run "git log fetch_head..head --oneline" 9 | (λ (out in err) 10 | (define logs 11 | (string-join (sequence->list (in-lines out)) 12 | "\n")) 13 | (define result 14 | (message-box/custom "Push Commits" 15 | logs 16 | command 17 | "cancel" 18 | #f)) 19 | (match result 20 | [1 (run (format "git ~a" command))] 21 | [2 (void)])))) 22 | 23 | (module+ main 24 | (require framework/preferences) 25 | 26 | (preferences:set-default 'current-project (current-directory) path-string?) 27 | (make-pusher "git push")) 28 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | (define collection "sauron") 3 | (define deps 4 | '("base" "gui-lib" 5 | "net-lib" 6 | "data-lib" 7 | "drracket-plugin-lib" 8 | "drracket-tool-lib" 9 | "file-watchers" 10 | "raco-invoke" 11 | ; syntax 12 | "try-catch-finally-lib" 13 | "curly-fn-lib" 14 | ; bundle 15 | "raco-new" 16 | "drcomplete")) 17 | (define build-deps '("scribble-lib" "racket-doc" "rackunit-lib" "gui-doc")) 18 | (define scribblings '(("scribblings/sauron.scrbl" (multi-page) ("DrRacket Plugins")))) 19 | (define pkg-desc "A Racket IDE") 20 | (define version "1.5.1") 21 | (define license '(Apache-2.0 OR MIT)) 22 | (define pkg-authors '(dannypsnl)) 23 | 24 | (define drracket-tools 25 | '(("tool/bind-key.rkt") ("tool/frame.rkt") ("tool/editor.rkt") ("tool/repl.rkt"))) 26 | (define drracket-tool-names '("sauron:keyword" "sauron:unit" "sauron:editor" "sauron:repl")) 27 | (define drracket-tool-icons '(#f #f #f #f)) 28 | -------------------------------------------------------------------------------- /log.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide log:debug 4 | log:info 5 | log:warning 6 | log:error) 7 | 8 | (require racket/logging 9 | racket/list 10 | "path/util.rkt") 11 | 12 | (define port (open-output-file (build-path config-dir "debug-log") 13 | #:exists 'append)) 14 | 15 | (define (write-log level msg args) 16 | (with-logging-to-port port 17 | (λ () 18 | (define fmt-msg (format "[~a] ~a" level msg)) 19 | (if (empty? args) 20 | (log-message (current-logger) 21 | level #f 22 | fmt-msg #f) 23 | (log-message (current-logger) 24 | level #f 25 | (apply format fmt-msg args) #f))) 26 | level)) 27 | 28 | (define (log:debug format . args) 29 | (write-log 'debug format args)) 30 | (define (log:info format . args) 31 | (write-log 'info format args)) 32 | (define (log:warning format . args) 33 | (write-log 'warning format args)) 34 | (define (log:error format . args) 35 | (write-log 'error format args)) 36 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Lîm Tsú-thuàn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /path/ignore.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide ignore?) 3 | (require file/glob 4 | racket/path 5 | framework/preferences) 6 | 7 | (define ignore-list '(".*" 8 | ".*/**" 9 | "compiled" 10 | "compiled/**" 11 | "coverage" 12 | "coverage/**" 13 | "doc" 14 | "doc/**")) 15 | 16 | (define (ignore? path) 17 | (define proj-dir (preferences:get 'current-project)) 18 | (glob-match? ignore-list (if proj-dir 19 | (find-relative-path proj-dir path) 20 | path))) 21 | 22 | (module+ test 23 | (require rackunit) 24 | 25 | (define test-layer (preferences:new-layer (preferences:current-layer))) 26 | (parameterize ([preferences:current-layer test-layer]) 27 | (preferences:set-default 'current-project (current-directory) path-string?) 28 | 29 | (check-true (ignore? ".DS_Store")) 30 | (check-true (ignore? ".git")) 31 | (check-true (ignore? ".git/index")) 32 | (check-true (ignore? ".git/info/exclude")) 33 | (check-true (ignore? "compiled/info_rkt.dep")))) 34 | -------------------------------------------------------------------------------- /path/renamer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide auto-rename) 4 | 5 | (require racket/gui 6 | "../log.rkt" 7 | "../collect/api.rkt") 8 | 9 | (define (auto-rename dir editor-panel 10 | old-path new-path) 11 | (rename-file-or-directory old-path new-path) 12 | 13 | (define racket-files (find-files (lambda (p) (path-has-extension? p #".rkt")) 14 | ; start from given dir 15 | dir)) 16 | (for/async ([f racket-files]) 17 | (define to-update-loc (require-location? f old-path)) 18 | (when to-update-loc 19 | (match-define (list start end) to-update-loc) 20 | (define t (new text%)) 21 | (send t load-file f) 22 | (send t insert (string-append "\"" 23 | (path->string (find-relative-path (path-only f) new-path)) 24 | "\"") 25 | start end) 26 | (send t save-file) 27 | (define tab (send editor-panel find-matching-tab f)) 28 | (when tab 29 | (send (send tab get-defs) load-file f)) 30 | (log:debug "~a get updated, since ~a get renamed to ~a" f old-path new-path)))) 31 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | paths: 8 | - "**test.yml" 9 | - "**.rkt" 10 | - "**.scrbl" 11 | pull_request: 12 | branches: 13 | - "*" 14 | paths: 15 | - "**.rkt" 16 | - "**.scrbl" 17 | # Allows you to run this workflow manually from the Actions tab 18 | workflow_dispatch: 19 | 20 | jobs: 21 | test: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | matrix: 25 | variant: ["CS"] 26 | version: ["stable", "current"] 27 | steps: 28 | - uses: actions/checkout@master 29 | - name: Cache installed packages 30 | uses: actions/cache@v4 31 | with: 32 | path: | 33 | ~/.cache/racket 34 | ~/.local/share/racket 35 | key: ${{ runner.os }}-primes 36 | - name: Setup Racket 37 | uses: Bogdanp/setup-racket@v1.14 38 | with: 39 | architecture: "x64" # (x64 or x86), ignored on Linux 40 | distribution: "minimal" 41 | variant: ${{ matrix.variant }} 42 | version: ${{ matrix.version }} 43 | - run: raco pkg install --auto --skip-installed 44 | - run: xvfb-run -a raco test . 45 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | on: 3 | push: 4 | branches: 5 | - "*" 6 | paths: 7 | - "**coverage.yml" 8 | - "**.rkt" 9 | - "**.scrbl" 10 | pull_request: 11 | branches: 12 | - "*" 13 | paths: 14 | - "**.rkt" 15 | - "**.scrbl" 16 | # Allows you to run this workflow manually from the Actions tab 17 | workflow_dispatch: 18 | 19 | jobs: 20 | coverage: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@master 24 | - name: Setup Racket 25 | uses: Bogdanp/setup-racket@v1.14 26 | with: 27 | architecture: x64 28 | distribution: full 29 | variant: "CS" 30 | version: "stable" 31 | packages: "cover, cover-badge" 32 | - run: raco pkg install --no-docs --auto --skip-installed 33 | - run: raco pkg install --auto --skip-installed 34 | - run: | 35 | xvfb-run -a raco cover -n tool -b . 36 | raco cover-badge 37 | - name: Deploy 38 | uses: peaceiris/actions-gh-pages@v3 39 | with: 40 | github_token: ${{ secrets.GITHUB_TOKEN }} 41 | publish_dir: coverage 42 | publish_branch: gh-pages 43 | destination_dir: coverage 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sauron 2 | 3 | [![Test](https://github.com/dannypsnl/sauron/actions/workflows/test.yml/badge.svg)](https://github.com/dannypsnl/sauron/actions/workflows/test.yml) 4 | [![Coverage Status](https://badgen.net/https/dannypsnl.github.io/sauron/coverage/badge.json)](https://dannypsnl.github.io/sauron/coverage) 5 | 6 | Sauron is a plugin of DrRacket to make it be a better Racket IDE, see more in [user guide][user-guide], the following shows something you can do with the plugin. 7 | 8 | 1. Refactoring 9 | 2. File explorer 10 | 3. Auto formatting 11 | 4. Jump to definition 12 | 5. Execute selection, enclosed, or nearest expression 13 | 6. ... 14 | 15 | ### Install 16 | 17 | ```sh 18 | raco pkg install --auto sauron 19 | ``` 20 | 21 | After installation, restart your DrRacket is required, then go to [user guide][user-guide] to see how to use it. 22 | 23 | ### Other plugins 24 | 25 | - [drcomplete](https://github.com/yjqww6/drcomplete): this plugin can help you get better completion, with sauron, completion would be triggered by default gives you a better experience. NOTE: only recommend for >=8.0 users since performance issue 26 | 27 | [user-guide]: https://docs.racket-lang.org/sauron/user-guide.html 28 | 29 | ### Development 30 | 31 | Install Git Hooks 32 | 33 | ```sh 34 | ln -sf $(pwd)/.hooks/pre-commit $(pwd)/.git/hooks/pre-commit 35 | ``` 36 | -------------------------------------------------------------------------------- /tool/repl.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (provide tool@) 3 | (require drracket/tool 4 | sauron/repl/history) 5 | 6 | (define-unit tool@ 7 | (import drracket:tool^) 8 | (export drracket:tool-exports^) 9 | 10 | (define (phase1) (void)) 11 | (define (phase2) (void)) 12 | 13 | (define drracket-repl-mixin 14 | (mixin (drracket:rep:text<%> (class->interface drracket:rep:text%)) () 15 | (super-new) 16 | 17 | (define prompt-pos 0) 18 | 19 | (define (refresh-prompt repl) 20 | (send repl set-position prompt-pos (send this last-position)) 21 | (send repl insert (current-selected-expression)) 22 | (send repl set-position prompt-pos)) 23 | 24 | (define/override (on-char e) 25 | (match (send e get-key-code) 26 | [#\return #:when (= (send this last-position) (send this get-start-position)) 27 | (new-history (send this get-text prompt-pos (send this last-position))) 28 | (super on-char e)] 29 | ['up #:when (= prompt-pos (send this get-start-position)) 30 | (increase-selected-index) 31 | (refresh-prompt this)] 32 | ['down #:when (= prompt-pos (send this get-start-position)) 33 | (decrease-selected-index) 34 | (refresh-prompt this)] 35 | [else (super on-char e) 36 | (let ([new-pos (send this get-start-position)]) 37 | (when (< new-pos prompt-pos) 38 | (send this set-position prompt-pos)))])) 39 | 40 | (define/override (insert-prompt) 41 | (super insert-prompt) 42 | (set! prompt-pos (send this last-position))))) 43 | 44 | (drracket:get/extend:extend-interactions-text drracket-repl-mixin)) 45 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Sauron 2 | 3 | First of all, thanks for taking time to contribute! 4 | 5 | The following text would describe how to contribute to Sauron. I would keep this document in short, to help everyone finish it. 6 | 7 | ### Code of Conduct 8 | 9 | This project and everyone participating in it is governed by the [Sauron Code of Conduct](https://github.com/racket-tw/sauron/blob/master/CODE_OF_CONDUCT.md). 10 | By participating, you are expected to uphold this code. Please report unacceptable behavior to dannypsnl@gmail.com. 11 | 12 | ### Code Standard 13 | 14 | I actually didn't care about code standard, but contributor can follow [Racket Style Guide](https://docs.racket-lang.org/style/index.html) if you want. 15 | Let's see if need more specific standard in the future. 16 | 17 | NOTE: Prefer comment rather than **smart** code. 18 | 19 | #### Commit Standard 20 | 21 | We provide a template, you can apply it via the following command. 22 | 23 | ```sh 24 | git config commit.template $(pwd)/.gitmessage 25 | ``` 26 | 27 | The following is a real example. 28 | 29 | ``` 30 | [editor:bug] disable auto wrap to avoid bug 31 | 32 | auto formatter will remove whitespace even that is created by auto-wrap, 33 | thus, we have to disable auto-wrap for now. In the future, we may find 34 | out better solution to fix this 35 | 36 | fix #158 37 | ``` 38 | 39 | ### PR Standard 40 | 41 | Since this is a GUI project, automatically testing everything is impossible, I would believe people the functionality they made already tested. 42 | 43 | In a PR, make sure you: 44 | 45 | 1. clearly explain purpose(which feature, any tricky hack need to record in document for contributors?) 46 | 2. correctly refer to related issue(if there has no issue, create one) 47 | 3. update document if PR 48 | 1. related to key binding 49 | 2. create a new window 50 | 3. changing behavior 51 | 52 | ### Release Standard 53 | 54 | 1. create release branch 55 | 2. bump up `info.rkt` version 56 | 3. create PR with title `[release] vx.y.z` 57 | 4. squash merge 58 | 5. create new release 59 | -------------------------------------------------------------------------------- /meta.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide latex-complete) 4 | 5 | (define latex-complete 6 | #hash(("\\" . "\\") 7 | ;;; logic 8 | ("forall" . "∀") 9 | ("all" . "∀") 10 | ("ex" . "∃") 11 | ("nex" . "∄") 12 | ("!=" . "≠") 13 | ("neq" . "≠") 14 | ("==" . "≡") 15 | ("equiv" . "≡") 16 | ("~=" . "≌") 17 | ("cong" . "≌") 18 | ("land" . "∧") 19 | ("lor" . "∨") 20 | ("neg" . "¬") 21 | ("top" . "⊤") 22 | ("bot" . "⊥") 23 | ("|-" . "⊢") 24 | ("n|-" . "⊬") 25 | ("-|" . "⊣") 26 | ("qed" . "∎") 27 | ;;; set 28 | ("0" . "∅") 29 | ("in" . "∈") 30 | ("nin" . "∉") 31 | ("cap" . "∩") 32 | ("intersect" . "∩") 33 | ("cup" . "∪") 34 | ("union" . "∪") 35 | ;;; arrow 36 | ("->" . "→") 37 | ("to" . "→") 38 | ("=>" . "⇒") 39 | (">>" . "≫") 40 | ("<-" . "←") 41 | ("<=" . "⇐") 42 | ("<->" . "↔") 43 | ("<=>" . "⇔") 44 | ("m>" . "↦") 45 | ("-->" . "⟶") 46 | ("u>" . "↑") 47 | ("U>" . "⇑") 48 | ("d>" . "↓") 49 | ("D>" . "⇓") 50 | ;;; greek 51 | ("ga" . "α") 52 | ("alpha" . "α") 53 | ("gb" . "β") 54 | ("beta" . "β") 55 | ("gd" . "δ") 56 | ("Gd" . "Δ") 57 | ("ge" . "ε") 58 | ("gg" . "γ") 59 | ("Gg" . "Γ") 60 | ("Gamma" . "Γ") 61 | ("gh" . "η") 62 | ("gi" . "ι") 63 | ("gk" . "κ") 64 | ("gl" . "λ") 65 | ("lam" . "λ") 66 | ("lambda" . "λ") 67 | ("Gl" . "Λ") 68 | ("gm" . "μ") 69 | ("gn" . "ν") 70 | ("go" . "ω") 71 | ("Go" . "Ω") 72 | ("gp" . "π") 73 | ("pi" . "π") 74 | ("Gp" . "Π") 75 | ("Pi" . "Π") 76 | ("gr" . "ρ") 77 | ("gs" . "σ") 78 | ("Gs" . "Σ") 79 | ("Sigma" . "Σ") 80 | ("gt" . "τ") 81 | ("gv" . "ν") 82 | ("Gv" . "Υ") 83 | ("gw" . "ϕ") 84 | ("Gw" . "Φ") 85 | ("gx" . "χ") 86 | ("gy" . "ψ") 87 | ("Gy" . "Ψ") 88 | ("gz" . "ζ") 89 | ("zeta" . "ζ") 90 | ;;; arith 91 | ("times" . "×") 92 | ("div" . "÷") 93 | ;;; misc 94 | (":" . "∶") 95 | ("::" . "∷"))) 96 | -------------------------------------------------------------------------------- /repl/history.rkt: -------------------------------------------------------------------------------- 1 | ;; history selection part 2 | #lang racket/base 3 | 4 | (provide increase-selected-index decrease-selected-index 5 | new-history 6 | current-selected-expression) 7 | 8 | ;; evaluated-history: (Listof String) 9 | (define evaluated-history (make-parameter '())) 10 | ;; current-selected-index: Integer 11 | (define current-selected-index (make-parameter -1)) 12 | (define (new-history e) 13 | (evaluated-history (cons e (evaluated-history))) 14 | (current-selected-index -1)) 15 | (define (decrease-selected-index) 16 | (define new-val (sub1 (current-selected-index))) 17 | (when (>= new-val -1) 18 | (current-selected-index new-val))) 19 | (define (increase-selected-index) 20 | (define new-val (add1 (current-selected-index))) 21 | (when (< new-val (length (evaluated-history))) 22 | (current-selected-index new-val))) 23 | (define (current-selected-expression) 24 | (if (not (eq? (current-selected-index) -1)) 25 | (list-ref (evaluated-history) (current-selected-index)) 26 | "")) 27 | 28 | (module+ test 29 | (require rackunit) 30 | 31 | (test-case 32 | "new-history would reset selected index" 33 | (parameterize ([current-selected-index -1] 34 | [evaluated-history '("1" "1")]) 35 | ;; notice the bound check that `increase-selected-index` shouldn't bigger than size of history 36 | (increase-selected-index) 37 | (increase-selected-index) 38 | (check-equal? (current-selected-index) 1) 39 | (new-history "1") 40 | (check-equal? (current-selected-index) -1))) 41 | 42 | (test-case 43 | "smallest index is -1" 44 | (parameterize ([current-selected-index -1] 45 | [evaluated-history '()]) 46 | (decrease-selected-index) 47 | (decrease-selected-index) 48 | (check-equal? (current-selected-index) -1))) 49 | 50 | (test-case 51 | "largest index is a valid ref of history" 52 | (parameterize ([current-selected-index -1] 53 | [evaluated-history '("1" "1" "1")]) 54 | (increase-selected-index) 55 | (increase-selected-index) 56 | (increase-selected-index) 57 | (increase-selected-index) 58 | (check-equal? (current-selected-index) 2))) 59 | 60 | (test-case 61 | "current-selection returns empty string when no selected" 62 | (parameterize ([current-selected-index -1] 63 | [evaluated-history '("1" "1" "1")]) 64 | (check-equal? (current-selected-expression) "")))) 65 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at dannypsnl@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /tool/editor.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | (provide tool@) 3 | (require drracket/tool 4 | framework) 5 | 6 | (define-unit tool@ 7 | (import drracket:tool^) 8 | (export drracket:tool-exports^) 9 | 10 | (define (phase1) (void)) 11 | (define (phase2) (void)) 12 | 13 | (define drracket-editor-mixin 14 | (mixin (drracket:unit:definitions-text<%> racket:text<%>) () 15 | (super-new) 16 | 17 | (define/augment (on-save-file filename format) 18 | (remove-trailing-whitespace-all) 19 | (send this tabify-all)) 20 | 21 | (define/private (remove-trailing-whitespace-all) (remove-trailing-whitespace-select 0 (send this last-position))) 22 | (define/private (remove-trailing-whitespace-select [start (send this get-start-position)] 23 | [end (send this get-end-position)]) 24 | (define first-para (send this position-paragraph start)) 25 | (define end-para (send this position-paragraph end)) 26 | (define modifying-multiple-paras? (not (= first-para end-para))) 27 | (with-handlers ([exn:break? 28 | (λ (x) #t)]) 29 | (dynamic-wind 30 | (λ () 31 | (when (< first-para end-para) 32 | (begin-busy-cursor)) 33 | (send this begin-edit-sequence)) 34 | (λ () 35 | (define skip-this-line? #f) 36 | (let loop ([para first-para]) 37 | (when (<= para end-para) 38 | (define start (send this paragraph-start-position para)) 39 | (define end (send this paragraph-end-position para)) 40 | (for ([i (range start (add1 end))] 41 | #:when (and (char=? #\" (send this get-character i)) 42 | (not (char=? #\\ (send this get-character (sub1 i)))))) 43 | (set! skip-this-line? (not skip-this-line?))) 44 | (set! skip-this-line? (and modifying-multiple-paras? 45 | skip-this-line?)) 46 | (unless skip-this-line? 47 | (remove-trailing-whitespace start)) 48 | (parameterize-break #t (void)) 49 | (loop (add1 para)))) 50 | (when (and (>= (send this position-paragraph start) end-para) 51 | (<= (send this skip-whitespace (send this get-start-position) 'backward #f) 52 | (send this paragraph-start-position first-para))) 53 | (send this set-position 54 | (let loop ([new-pos (send this get-start-position)]) 55 | (if (let ([next (send this get-character new-pos)]) 56 | (and (char-whitespace? next) 57 | (not (char=? next #\newline)))) 58 | (loop (add1 new-pos)) 59 | new-pos))))) 60 | (λ () 61 | (send this end-edit-sequence) 62 | (when (< first-para end-para) 63 | (end-busy-cursor)))))) 64 | (define (remove-trailing-whitespace [pos (send this get-start-position)]) 65 | (define line (send this position-line pos)) 66 | (define line-start (send this line-start-position line)) 67 | (define line-end (send this line-end-position line)) 68 | (define (do-remove line) 69 | (define para (send this position-paragraph pos)) 70 | (define end (send this paragraph-start-position para)) 71 | (send this delete line-start line-end) 72 | (send this insert (string-trim line #px"\\s+" #:left? #f) end)) 73 | (do-remove (send this get-text line-start line-end))))) 74 | 75 | (drracket:get/extend:extend-definitions-text drracket-editor-mixin)) 76 | -------------------------------------------------------------------------------- /scribblings/user-guide.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | 3 | @title[#:tag "user-guide"]{User Guide: As DrRacket Plugin} 4 | 5 | In sauron, all @litchar{cmd}/@litchar{ctrl} would be called @litchar{c}, @litchar{alt}/@litchar{option} called @litchar{o}. 6 | 7 | @itemlist[ 8 | @item{@litchar{c+e} run whole file and show result in REPL panel} 9 | @item{@litchar{c+enter} run selection, wrapper, next, or previous expression in REPL} 10 | ;;; project management 11 | @item{@litchar{c+m} open project manager} 12 | @item{@litchar{c+y} show/hide project files viewer (Linux, MacOS only)} 13 | @item{@litchar{c+s+y} show/hide project files viewer (Windows only)} 14 | ;;; version control 15 | @item{@litchar{c+k} version control, open commit editor} 16 | @item{@litchar{c+s+k} commits push} 17 | @item{@litchar{c+s+p} commits pull} 18 | ;;; refactor 19 | @item{@litchar{c+r} rename all bound} 20 | ;;; jump to definition 21 | @item{@litchar{c+b} or @litchar{} jump to definition if cursor is on a reference/binding; open a list-box to let you pick a reference if cursor is on a definition} 22 | @item{@litchar{c+s+b} jump back to previous position} 23 | ;;; open document 24 | @item{@litchar{c+d} open documentation} 25 | ;;; edit 26 | @item{@litchar{c+backspace} delete whole line from current position} 27 | @item{@litchar{o+backspace} delete previous sexp} 28 | @item{@litchar{c+x} cut line if no selection, else cut selection} 29 | ; comment/uncomment 30 | @item{@litchar{c+;} comment selected text or line if uncommented, uncomment if commented} 31 | ; auto complete pair 32 | @item{@litchar["("]/@litchar["["]/@litchar["{"]/@litchar{"} when has selected text, wrap selected text automatically} 33 | ] 34 | 35 | @section{Editor} 36 | 37 | Editor can auto formatting your racket file. 38 | 39 | @section{Panel: REPL} 40 | 41 | REPL panel helps users quickly testing their ideas, it has a few key bindings can work on it: 42 | 43 | @itemlist[ 44 | @item{@litchar{} evaluate and save expression into evaluated history(also reset selected status to no selection)} 45 | @item{@litchar{} switch to previous expression in history} 46 | @item{@litchar{} switch to next expression in history} 47 | ] 48 | 49 | @section{Version Control} 50 | 51 | You can use @litchar{c+k} open version control panel, once open the panel, it has two part: 52 | 53 | @subsection{commit message editor} 54 | 55 | You can type commit message in this editor, use c+ to commit all ready files. 56 | 57 | @subsection{Changed files} 58 | 59 | Changed files would show below of the commit editor, they were clickable. Clicked means ready to commit, else is not. 60 | 61 | It has three buttons for quick modify as the following list. 62 | 63 | @itemlist[ 64 | @item{select all} 65 | @item{unselect all} 66 | @item{clean up} 67 | ] 68 | 69 | @subsection{Push/Pull} 70 | 71 | @itemlist[ 72 | @item{@litchar{c+s+k} commits push} 73 | @item{@litchar{c+s+p} commits pull} 74 | ] 75 | 76 | @section{Project Management} 77 | 78 | @subsection{Manager} 79 | 80 | @itemlist[ 81 | @item{add project(existed)} 82 | @item{create project(new one)} 83 | @item{remove project} 84 | @item{open project} 85 | ] 86 | 87 | @subsection{Viewer} 88 | 89 | @itemlist[ 90 | @item{interactive with files} 91 | @item{open file via double click} 92 | @item{add file/directory} 93 | @item{rename file/directory, a nice feature is those files imported it will get update} 94 | @item{remove file/directory} 95 | ] 96 | 97 | @section{Special symbol(LaTeX/Agda like) support} 98 | 99 | Sauron also supports converting input starting from @litchar{\} when entering the space key after these char sequences, this should be helpful for PLT/Math researchers. 100 | 101 | @(require "../meta.rkt") 102 | @(apply itemlist 103 | (for/list ([(cmd output-symbol) (in-hash latex-complete)]) 104 | @(item (litchar (format "\\~a" cmd)) " converts to " (litchar output-symbol)))) 105 | -------------------------------------------------------------------------------- /collect/record-maintainer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide create-record-maintainer 3 | get-record-maintainer 4 | terminate-record-maintainer) 5 | (require racket/path 6 | racket/future 7 | racket/function 8 | racket/match 9 | data/interval-map 10 | sauron/collect/record 11 | sauron/collect/collector 12 | sauron/log) 13 | 14 | (define (valid-path? file-path) 15 | (and (file-exists? file-path) 16 | (path-has-extension? file-path #".rkt"))) 17 | 18 | (define path=>maintainer (make-hash)) 19 | ;;; The constraint is only `record-maintainer-creator` allowed to create new maintainer 20 | ; but anyone might like to create one concurrently, so `hash-ref!` here blocked all repeated creation 21 | ; since `thread-receive` ensure every creation is proceeded one by one 22 | ; if the previous `loop` created one, the next `loop` will skip existing creation 23 | (define record-maintainer-creator 24 | (thread 25 | (thunk 26 | (let loop () 27 | (match (thread-receive) 28 | [(list 'create from path) 29 | (define mt (hash-ref! path=>maintainer path (thunk (make-record-maintainer path)))) 30 | ; `from` must be another thread 31 | (when from 32 | (thread-send from mt))]) 33 | (loop))))) 34 | 35 | (define (create-record-maintainer path) 36 | ; only create maintainer for valid path 37 | (when (valid-path? path) 38 | (future (thunk (hash-set! path=>maintainer path (thunk (make-record-maintainer path))))))) 39 | 40 | (define (get-record-maintainer path #:wait? [wait? #f]) 41 | (cond 42 | [(not (valid-path? path)) 43 | ; when path is invalid, return same thread to handle all non-sense requirement 44 | ; since only one-more thread here, it should not be a big overhead 45 | (log:warning "cannot create maintainer for invalid path: ~a" path) 46 | do-nothing] 47 | [wait? (thread-send record-maintainer-creator 48 | (list 'create (current-thread) path)) 49 | (thread-receive)] 50 | [else (hash-ref path=>maintainer path #f)])) 51 | 52 | (define (terminate-record-maintainer path) 53 | (when (valid-path? path) 54 | (define maintainer (get-record-maintainer path)) 55 | (when maintainer 56 | (hash-set! path=>maintainer path #f) 57 | (kill-thread maintainer)))) 58 | 59 | ;;; this thread do nothing and provide fake reply is need 60 | ; the purpose is making sure the caller will fail gratefully, but no need to handle exception 61 | ; this is because the caller already think cannot fetch data is normal 62 | ; in editor, users can always try to get jump to definition even no definition exists 63 | ; so caller will just ignore the operation, thus, another error handling shouldn't be there 64 | (define do-nothing (thread (thunk (let loop () 65 | (match (thread-receive) 66 | [(list 'get-record from) 67 | (thread-send from (make-record))] 68 | [else (void)]) 69 | (loop))))) 70 | 71 | (define (make-record-maintainer file-path) 72 | (thread 73 | (thunk 74 | (define ns (make-base-namespace)) 75 | (define cached-record (collect-from file-path ns)) 76 | (let loop () 77 | (match (thread-receive) 78 | [(list 'update) 79 | (match-define (struct* record ([created-time created-time])) cached-record) 80 | (when (< created-time (file-or-directory-modify-seconds file-path)) 81 | (set! cached-record (collect-from file-path ns)))] 82 | 83 | [(list 'require-location? from require) 84 | (define requires (record-requires cached-record)) 85 | (thread-send from (hash-ref requires require #f))] 86 | 87 | [(list 'get-doc from pos) 88 | (define doc (record-doc cached-record)) 89 | (thread-send from (interval-map-ref doc pos #f))] 90 | 91 | [(list 'get-def from pos) 92 | (define defs (record-defs cached-record)) 93 | (thread-send from (interval-map-ref defs pos #f))] 94 | ) 95 | (loop))))) 96 | -------------------------------------------------------------------------------- /tool/frame.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (provide tool@) 3 | (require drracket/tool 4 | framework 5 | racket/gui/base 6 | 7 | sauron/project/manager 8 | sauron/project/panel 9 | sauron/log) 10 | 11 | (define-unit tool@ 12 | (import drracket:tool^) 13 | (export drracket:tool-exports^) 14 | 15 | (define (phase1) 16 | (preferences:set-default 'current-project 17 | #f 18 | (λ (v) (or (path-string? v) (false? v)))) 19 | (preferences:add-callback 'current-project 20 | (λ (_ new-dir) 21 | (log:info "current project is ~a" new-dir)))) 22 | (define (phase2) (void)) 23 | 24 | (define drracket-frame-mixin 25 | (mixin (drracket:unit:frame<%> (class->interface drracket:unit:frame%)) () 26 | (define project-files-show? #f) 27 | 28 | (super-new) 29 | 30 | (define/override (get-definitions/interactions-panel-parent) 31 | (define panel (new panel:horizontal-dragable% [parent (super get-definitions/interactions-panel-parent)])) 32 | (define real-area (new panel:vertical-dragable% [parent panel])) 33 | 34 | (new project-files-pane% [parent real-area] 35 | [editor-panel this]) 36 | (send real-area set-percentages '(1/20 19/20)) 37 | 38 | (define (close-real-area) 39 | (set! project-files-show? #f) 40 | (send panel change-children 41 | (λ (x) 42 | (filter 43 | (λ (x) (not (eq? real-area x))) x)))) 44 | (define (show-real-area) 45 | (set! project-files-show? #t) 46 | (send panel change-children 47 | (λ (x) (cons real-area x))) 48 | (send panel set-percentages '(2/11 9/11))) 49 | (new menu-item% [parent (send this get-show-menu)] 50 | [label (if project-files-show? "Hide the Project Viewer" "Show the Project Viewer")] 51 | [callback 52 | (λ (c e) 53 | (define (get-manager) 54 | (new project-manager% 55 | [label "select a project"] 56 | [on-select 57 | (λ (path) 58 | (preferences:set 'current-project path) 59 | (show-real-area) 60 | (send c set-label "Hide the Project Viewer"))])) 61 | (if (preferences:get 'current-project) 62 | (cond 63 | [project-files-show? 64 | (close-real-area) 65 | (send c set-label "Show the Project Viewer")] 66 | [else 67 | (show-real-area) 68 | (send c set-label "Hide the Project Viewer")]) 69 | (send (get-manager) run)))] 70 | ;;; c+y open project viewer (on Linux, MacOS) 71 | ;;; c+s+y open project viewer (on Windows) 72 | [shortcut #\y] 73 | [shortcut-prefix (case (system-type) 74 | [(windows) '(ctl shift)] 75 | [else (get-default-shortcut-prefix)])]) 76 | 77 | (let ([edit-menu (send this get-edit-menu)]) 78 | (for ([item (send edit-menu get-items)] 79 | #:when (and (is-a? item labelled-menu-item<%>) (equal? "Find" (send item get-label)))) 80 | (send item delete)) 81 | (new menu-item% [parent edit-menu] 82 | [label "Find"] 83 | [callback (λ (c e) 84 | (if (send this search-hidden?) 85 | (send this unhide-search-and-toggle-focus 86 | #:new-search-string-from-selection? #t) 87 | (send this hide-search)))] 88 | ;;; c+f search text 89 | [shortcut #\f] 90 | [shortcut-prefix (get-default-shortcut-prefix)])) 91 | 92 | (unless project-files-show? 93 | (send panel change-children 94 | (λ (x) 95 | (filter 96 | (λ (x) (not (eq? real-area x))) x)))) 97 | (make-object vertical-panel% panel)))) 98 | 99 | (drracket:get/extend:extend-unit-frame drracket-frame-mixin)) 100 | -------------------------------------------------------------------------------- /collect/api.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | (provide start-tracking 3 | require-location? 4 | get-doc 5 | get-def 6 | update 7 | create 8 | show-references) 9 | (require "record-maintainer.rkt" 10 | "collector.rkt" 11 | "../log.rkt") 12 | 13 | (define (start-tracking directory ignore?) 14 | ; NOTE: `fold-files` reduces about 100MB compare with `find-files` 15 | ; this is reasonable, since `find-files` build a huge list 16 | (fold-files (lambda (path kind acc) 17 | (cond 18 | [(ignore? path) (values acc #f)] 19 | ; NOTE: should I simply assume `*.rkt` is not a ignored file? 20 | [(path-has-extension? path #".rkt") 21 | (create path) 22 | acc] 23 | [else acc])) 24 | #f 25 | directory 26 | #t)) 27 | 28 | ;;; just prepare a maintainer for a path 29 | (define (create path) 30 | (create-record-maintainer path)) 31 | ;;; tell corresponding maintainer update the record 32 | (define (update path) 33 | (thread-send (get-record-maintainer path #:wait? #t) 34 | (list 'update))) 35 | 36 | ; require-location? : path path -> list 37 | (define (require-location? path require) 38 | (thread-send (get-record-maintainer path #:wait? #t) 39 | (list 'require-location? 40 | (current-thread) 41 | require)) 42 | (thread-receive)) 43 | ; get-doc : path pos:exact-integer? -> string 44 | (define (get-doc path pos) 45 | (thread-send (get-record-maintainer path #:wait? #t) 46 | (list 'get-doc 47 | (current-thread) 48 | pos)) 49 | (thread-receive)) 50 | ; get-def : path pos:exact-integer? -> (or symbol #f) 51 | (define (get-def path pos) 52 | (thread-send (get-record-maintainer path #:wait? #t) 53 | (list 'get-def 54 | (current-thread) 55 | pos)) 56 | (thread-receive)) 57 | 58 | ;; Show references popup list-box 59 | (define (show-references editor filename id [parent #f]) 60 | (define key (list filename id)) 61 | (define references (dict-ref projectwise-references key (set))) 62 | 63 | (cond 64 | [(set-empty? references) 65 | (message-box "No References" (format "No references found for ~a in ~a" id filename))] 66 | [else 67 | (define references-choice-frame 68 | (new frame% [label (format "References of ~a" id)] [width 600] [height 400] [parent parent])) 69 | (define refs (set->list references)) 70 | (define choices 71 | (for/list ([ref-info (in-list refs)]) 72 | (match-define (list ref-file start _end) ref-info) 73 | (define line (send editor position-line start)) 74 | (define line-sp (send editor line-start-position line)) 75 | (format "~a:~a:~a" (path->string ref-file) line (- start line-sp)))) 76 | 77 | (define list-box 78 | (new list-box% 79 | [parent references-choice-frame] 80 | [label "References:"] 81 | [choices choices] 82 | [style '(single)] 83 | [callback 84 | (lambda (lb event) 85 | (when (eq? (send event get-event-type) 'list-box-dclick) 86 | (define selection (send lb get-selection)) 87 | (when selection 88 | (match-define (list ref-file start end) (list-ref refs selection)) 89 | (send references-choice-frame show #f) 90 | ; jump to picked reference location 91 | (define editor-frame (send+ editor (get-tab) (get-frame))) 92 | (prepare-editor-for editor-frame ref-file) 93 | (send+ editor-frame (get-editor) (set-position start end)) 94 | (define line (send editor position-line start)) 95 | (define line-sp (send editor line-start-position line)) 96 | (log:info "Jump to reference ~a:~a:~a" ref-file line (- start line-sp)))))])) 97 | 98 | (send references-choice-frame center) 99 | (send references-choice-frame show #t) 100 | references-choice-frame])) 101 | 102 | (define (prepare-editor-for frame path) 103 | (define tab-of-path- (send frame find-matching-tab path)) 104 | (if tab-of-path- 105 | ; when we already have a tab for the path, switch to it 106 | (send frame change-to-tab tab-of-path-) 107 | ; when we don't have a tab for the path, open one 108 | (send frame open-in-new-tab path))) 109 | -------------------------------------------------------------------------------- /project/manager.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | (provide project-manager%) 3 | (require raco/invoke 4 | "../path/util.rkt" 5 | "project-templates.rkt") 6 | 7 | (define project-manager% 8 | (class frame% 9 | (init-field on-select) 10 | (super-new [width 600] [height 600]) 11 | 12 | ;;; auto setup configuration 13 | (define projects-file (build-path config-dir "projects")) 14 | 15 | (define (get-projects) (file->lines projects-file #:mode 'text)) 16 | 17 | (define (auto-setup-configuration-env) 18 | ; create projects file configuration if not existed 19 | (unless (file-exists? projects-file) 20 | (display-to-file "" projects-file))) 21 | 22 | (define (add-project path) 23 | (call-with-output-file projects-file 24 | #:exists 'append 25 | (λ (port) 26 | (parameterize ([current-output-port port]) 27 | ; put path into config 28 | (displayln path) 29 | ; append into current selectable list 30 | (send list-box append (path->string path)))))) 31 | (define (remove-selected-project) 32 | ; for current single selection list-box, this method always returns a list contains one number or a null 33 | (define selection* (send list-box get-selections)) 34 | (unless (null? selection*) 35 | (let ([n (first selection*)]) 36 | ; 1. remove to delete item from list-box 37 | (send list-box delete n) 38 | (call-with-output-file projects-file 39 | #:exists 'truncate ; 2. truncate removes all data from config 40 | (λ (port) 41 | (parameterize ([current-output-port port]) 42 | ; 3. now write all paths in list-box back into config 43 | (for ([n (range (send list-box get-number))]) 44 | (displayln (send list-box get-string n))))))))) 45 | 46 | (define list-box 47 | (new list-box% [parent this] 48 | [label "projects"] 49 | [choices '()] 50 | [style '(single)] 51 | [callback 52 | (λ (proj-manager event) 53 | (define evt-type (send event get-event-type)) 54 | (match evt-type 55 | ['list-box-dclick 56 | (let ([str-path (send proj-manager get-string-selection)]) 57 | (when str-path 58 | (let ([path (string->path str-path)]) 59 | (if (directory-exists? path) 60 | (begin 61 | (send this show #f) 62 | (on-select path)) 63 | (begin 64 | (message-box "Failed" "project not existed") 65 | (remove-selected-project))))))] 66 | ['list-box 67 | (void)]))])) 68 | 69 | (new button% [parent this] 70 | [label "add project"] 71 | [callback 72 | (λ (btn event) 73 | (define path (get-directory #f this)) 74 | (when (and path 75 | (directory-exists? path) 76 | (not (member (path->string path) (get-projects)))) 77 | (add-project path)))]) 78 | 79 | (new button% [parent this] 80 | [label "create project"] 81 | [callback 82 | (λ (btn event) 83 | (define user-selected-path (get-directory "create at?")) 84 | (define project-name (get-text-from-user "name of project?" "")) 85 | (define tmp-frame (new frame% [label "template"] 86 | [height 600] [width 600])) 87 | (new list-box% [parent tmp-frame] 88 | [label "template"] 89 | [choices project-templates] 90 | [callback 91 | (λ (template-selection event) 92 | (define evt-type (send event get-event-type)) 93 | (match evt-type 94 | ['list-box-dclick 95 | (send tmp-frame show #f) 96 | (match-define (list n) 97 | (send template-selection get-selections)) 98 | (define path (build-path user-selected-path project-name)) 99 | (raco "new" 100 | (send template-selection get-string n) 101 | (path->string path)) 102 | (add-project path)] 103 | ['list-box (void)]))]) 104 | (send* tmp-frame 105 | [center] 106 | [show #t]))]) 107 | 108 | (new button% [parent this] 109 | [label "remove project"] 110 | [callback 111 | (λ (btn event) 112 | (remove-selected-project))]) 113 | 114 | (define (load-projects) 115 | (for ([project-path (get-projects)]) 116 | (send list-box append project-path))) 117 | 118 | (define/public (run) 119 | (auto-setup-configuration-env) 120 | (load-projects) 121 | (send* this 122 | [center 'both] 123 | [show #t])))) 124 | 125 | (module+ main 126 | (define starter (new project-manager% 127 | [label "select a project"] 128 | [on-select (λ (path) (message-box "dummy" (format "~a opened" path)))])) 129 | (send starter run)) 130 | -------------------------------------------------------------------------------- /collect/collector.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | 3 | (provide collect-from 4 | projectwise-references) 5 | 6 | (require drracket/check-syntax 7 | syntax/modread 8 | net/url 9 | data/interval-map 10 | try-catch-finally 11 | sauron/collect/record 12 | sauron/log) 13 | 14 | ;; Global reference map: (filename . id) -> list of (reference-file start end) 15 | (define projectwise-references (make-hash)) 16 | 17 | (define collector% 18 | (class (annotations-mixin object%) 19 | (init-field src text) 20 | 21 | (define doc (make-interval-map)) 22 | (define defs (make-interval-map)) 23 | (define requires (make-hash)) 24 | 25 | (define/override (syncheck:find-source-object stx) (and (equal? src (syntax-source stx)) src)) 26 | 27 | (define/override (syncheck:add-docs-menu source-obj 28 | start 29 | end 30 | id 31 | _label 32 | path 33 | definition-tag 34 | url-tag) 35 | (when url 36 | (when (= start end) 37 | (set! end (add1 end))) 38 | (define path-url (path->url path)) 39 | (define link+tag 40 | (cond 41 | [url-tag (struct-copy url path-url [fragment url-tag])] 42 | [definition-tag (struct-copy url path-url 43 | [fragment (def-tag->html-anchor-tag definition-tag)])] 44 | [else path-url])) 45 | (interval-map-set! doc start end (url->string link+tag)))) 46 | 47 | (define/override (syncheck:add-require-open-menu source-obj start end required-file) 48 | (log:debug "require ~a" required-file) 49 | (hash-set! requires required-file (list start end))) 50 | 51 | (define/override (syncheck:add-arrow/name-dup start-src-obj start-left start-right 52 | end-src-obj end-left end-right 53 | actual? 54 | level 55 | require-arrow? 56 | name-dup?) 57 | (unless require-arrow? 58 | (define id (string->symbol (send text get-text start-left start-right))) 59 | (define key (list src id)) 60 | (define reference-info (list src end-left end-right)) 61 | (dict-update! projectwise-references key 62 | (lambda (refs) (set-add refs reference-info)) 63 | (set)))) 64 | 65 | (define/override (syncheck:add-jump-to-definition source-obj start end id filename submods) 66 | (define key (list (or filename src) id)) 67 | (define reference-info (list src start end)) 68 | (dict-update! projectwise-references key 69 | (lambda (refs) (set-add refs reference-info)) 70 | (set))) 71 | 72 | (define/override (syncheck:add-definition-target source-obj start end id mods) 73 | ; interval map to find the symbol name of this range 74 | ; e.g. if I write down 75 | ; (define xxx ...) 76 | ; the range of `xxx` should map to `xxx` this symbol 77 | (log:debug "syncheck:add-definition-target ~a:~a" source-obj id) 78 | (interval-map-set! defs start end id)) 79 | 80 | (define/public (build-record) 81 | (make-record #:created-time (current-seconds) 82 | #:doc doc 83 | #:defs defs 84 | #:requires requires)) 85 | (super-new))) 86 | 87 | (define (collect-from path ns) 88 | (define text (new text%)) 89 | (send text load-file path) 90 | (define collector (new collector% [src path] [text text])) 91 | (define-values (src-dir file dir?) (split-path path)) 92 | (log:info "collect-from path: ~a" path) 93 | (define in (open-input-string (send text get-text))) 94 | 95 | (try (define-values (add-syntax done) (make-traversal ns src-dir)) 96 | (parameterize ([current-annotations collector] 97 | [current-namespace ns] 98 | [current-load-relative-directory src-dir]) 99 | (define stx (expand (with-module-reading-parameterization (λ () (read-syntax path in))))) 100 | (add-syntax stx) 101 | (done)) 102 | (log:info "collect-from path done: ~a" path) 103 | (catch _ (log:error "collect-from path: ~a failed" path))) 104 | (send collector build-record)) 105 | 106 | #| 107 | NOTICE: based on MIT/APACHE2.0 108 | modify from https://github.com/jeapostrophe/racket-langserver/blob/master/docs-helpers.rkt 109 | origin author: https://github.com/jeapostrophe/racket-langserver/graphs/contributors 110 | modifier author: Lîm Tsú-thuàn(GitHub: @dannypsnl) 111 | |# 112 | ;; Example: '(def ((quote #%kernel) hasheq)) => "(def._((quote._~23~25kernel)._hasheq))" 113 | ;; mostly a copy of a closed function `anchor-name` in `scribble-lib/scribble/html-render.rkt` 114 | (define (def-tag->html-anchor-tag v) 115 | (define (encode-byte b) 116 | (string-append (if (< b 16) "~0" "~") (number->string b 16))) 117 | (define (encode-bytes str) 118 | (string->bytes/utf-8 (encode-byte (bytes-ref str 0)))) 119 | (let* ([v (string->bytes/utf-8 (format "~a" v))] 120 | [v (regexp-replace* #rx#"[A-Z.]" v #".&")] 121 | [v (regexp-replace* #rx#" " v #"._")] 122 | [v (regexp-replace* #rx#"\"" v #".'")] 123 | [v (regexp-replace* #rx#"[^-a-zA-Z0-9_!+*'()/.,]" v encode-bytes)]) 124 | (bytes->string/utf-8 v))) 125 | 126 | (module+ main 127 | (define ns (make-base-namespace)) 128 | (record-doc (collect-from (normalize-path "collector.rkt") ns))) 129 | -------------------------------------------------------------------------------- /version-control/panel.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | 3 | (require framework 4 | sauron/cmd/execute 5 | sauron/version-control/parse-git 6 | sauron/log) 7 | 8 | (provide version-control%) 9 | (define version-control% 10 | (class panel:vertical-dragable% 11 | (super-new) 12 | 13 | ;;; commit editor 14 | (define editor-canvas (new editor-canvas% 15 | [parent this] 16 | [style '(no-hscroll)])) 17 | (define commit-editor% 18 | (class racket:text% 19 | (super-new) 20 | (inherit get-text 21 | erase) 22 | 23 | (define/override (on-char e) 24 | (match (send e get-key-code) 25 | [#\return #:when (send e get-meta-down) 26 | (run (format "git commit -m '~a'" (get-text))) 27 | (erase) 28 | ;;; after commit, we need to refresh files 29 | ; it's ok to commit without any ready files, in this case, all files be removed and added back later 30 | (for ([f (send files-zone get-children)]) 31 | (send files-zone delete-child f)) 32 | (update-status)] 33 | [else (super on-char e)])))) 34 | (define commit-message-editor (new commit-editor%)) 35 | (send editor-canvas set-editor commit-message-editor) 36 | 37 | ;;; ready/changes zone 38 | (define zone (new panel:vertical-dragable% [parent this])) 39 | (send this set-percentages (list 1/3 2/3)) 40 | (define button-zone (new horizontal-panel% [parent zone])) 41 | (define files-zone (new group-box-panel% [parent zone] 42 | [label "files"] 43 | [alignment '(left top)])) 44 | (send zone set-percentages (list 1/10 9/10)) 45 | 46 | (new button% [parent button-zone] 47 | [label "select all"] 48 | [callback 49 | (λ (btn event) 50 | (for ([file-obj (send files-zone get-children)]) 51 | (send file-obj add-to-ready)))]) 52 | (new button% [parent button-zone] 53 | [label "unselect all"] 54 | [callback 55 | (λ (btn event) 56 | (for ([file-obj (send files-zone get-children)]) 57 | (send file-obj remove-from-ready)))]) 58 | (new button% [parent button-zone] 59 | [label "clean up"] 60 | [callback 61 | (λ (btn event) 62 | (run "git reset --hard") 63 | (run "git clean -fd") 64 | (for ([f (send files-zone get-children)]) 65 | (send files-zone delete-child f)))]) 66 | 67 | (define/public (update-status) 68 | ; show current status one file one line 69 | (run "git status --short --untracked-files=all" 70 | (λ (out in err) 71 | (let loop ([output (read-line out)]) 72 | (unless (eof-object? output) 73 | (define-values (kind filename) (parse-git-output output)) 74 | (new file-object% [parent files-zone] 75 | [filename filename] 76 | [λ-add-to-ready 77 | (λ (this filename) 78 | (log:debug "add ~a to ready" filename) 79 | (run (format "git add ~a" (build-path (preferences:get 'current-project) filename))))] 80 | [λ-remove-from-ready 81 | (λ (this filename) 82 | (log:debug "remove ~a from ready" filename) 83 | (run (format "git reset HEAD ~a" (build-path (preferences:get 'current-project) filename))))] 84 | [status kind]) 85 | (loop (read-line out))))))) 86 | 87 | ;;; init 88 | (update-status))) 89 | 90 | (define file-object% 91 | (class horizontal-panel% 92 | (init-field filename 93 | λ-add-to-ready 94 | λ-remove-from-ready 95 | status) 96 | (super-new [alignment '(left top)]) 97 | 98 | (define/public (update-by-checkbox check-box) 99 | (if (send check-box get-value) 100 | (λ-add-to-ready this filename) 101 | (λ-remove-from-ready this filename))) 102 | 103 | (define check-box 104 | (new check-box% [parent this] 105 | [label filename] 106 | [value (match status 107 | ['ready #t] 108 | ['changes #f])] 109 | [callback 110 | (λ (check-box event) 111 | (update-by-checkbox check-box))])) 112 | 113 | (define/public (add-to-ready) 114 | (send check-box set-value #t) 115 | (update-by-checkbox check-box)) 116 | (define/public (remove-from-ready) 117 | (send check-box set-value #f) 118 | (update-by-checkbox check-box)))) 119 | 120 | (module+ main 121 | (require racket/runtime-path) 122 | 123 | (define-runtime-path testing-dir ".") 124 | (unless (directory-exists? testing-dir) 125 | (error 'file "no such dir")) 126 | 127 | (preferences:set-default 'current-project testing-dir path-string?) 128 | (define test-frame (new frame% 129 | [label "Version Control Panel"] 130 | [width 300] 131 | [height 600])) 132 | 133 | (define vc 134 | (new version-control% 135 | [parent test-frame])) 136 | 137 | (send test-frame center) 138 | (send test-frame show #t)) 139 | 140 | (module+ test 141 | (require rackunit) 142 | 143 | (test-case "file-object will be add to ready if clicked" 144 | (define frame (new frame% [label "test"])) 145 | (define ready-fo (new file-object% [parent frame] 146 | [filename ""] 147 | [λ-add-to-ready (λ (a b) (void))] 148 | [λ-remove-from-ready (λ (a b) (error 'remove))] 149 | [status 'ready])) 150 | (send ready-fo add-to-ready)) 151 | (test-case "file-object will be remove from ready if not clicked" 152 | (define frame (new frame% [label "test"])) 153 | (define ready-fo (new file-object% [parent frame] 154 | [filename ""] 155 | [λ-add-to-ready (λ (a b) (error 'remove))] 156 | [λ-remove-from-ready (λ (a b) (void))] 157 | [status 'ready])) 158 | (send ready-fo remove-from-ready))) 159 | 160 | 161 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### To Release 2 | 3 | - c+b/click now 4 | 1. jumps to definintion if cursor is on a reference/binding 5 | 2. toggle a list-box to pick references of the definition if cursor is on a definition 6 | 7 | Toggle list-box to pick a references 8 | 9 | Image 10 | 11 | Choose a reference at another file and jump 12 | 13 | Image 14 | 15 | ### v1.5.1 16 | 17 | - [editor:fix] c+enter in REPL won't crash now 18 | 19 | ### v1.5.0 20 | 21 | - c+enter execute selection, enclosed, or nearest expression 22 | - dependency: `from-template` -> `raco-new` 23 | - cache namespace in record maintainer (see #225) 24 | - stop ensure BC will work 25 | 26 | ### v1.4.1 27 | 28 | - [editor] fix a linux/windows binding bug that all alt/meta are actually wrong 29 | 30 | ### v1.4.0 31 | 32 | - [editor] c+s fix **save file** 33 | - [editor] c+b for untitled tab 34 | - [editor] c+s+t reopen recently closed tab 35 | - [project] remove all unnecessary refreshing 36 | - [project:fix] double click the empty space of project manager makes a contract violation 37 | - [project] huge speedup 38 | - [internal] file indexing maintaining system 39 | - [editor] `>>` produces `≫` 40 | 41 | ### v1.3.1 42 | 43 | - [editor:fix] c+d open document 44 | - [editor:fix] `space` will work for `text-field%` now 45 | - [editor:fix] `alt+backspace` will work for `text-field%` now 46 | 47 | ### v1.3.0 48 | 49 | - [shortcut:fix] `o+backspace` and `space` on improper editor 50 | - [preferences:fix] click "Undo Changes and Close" won't get crashed now 51 | - [project:fix] sauron now can work without network by using hard-coded list when no networking 52 | - [project] save file to current project by default 53 | - [project] rename file will update files imported it 54 | - [misc] bundle [drcomplete](https://github.com/yjqww6/drcomplete) 55 | - [preferences] expose `'current-project` in preferences, one can use `(preferences:get 'current-project)` for co-work plugin 56 | - [editor:fix] fix remove trailing whitespace problem in auto formatter 57 | 58 | ### v1.2.0 59 | 60 | - [editor:fix] type space in search bar won't crash now 61 | - [project] rename file/directory 62 | - [editor:fix] disable auto wrap line to avoid auto formatter break the line 63 | - [editor] auto formatting file 64 | - [editor] cross file jump to definition 65 | - [editor] c+s+b jump back to previous position 66 | - [editor] close untitled tab automatically when open new file 67 | 68 | ### v1.1.1 69 | 70 | - [fix] memory leak bug caused by unclosed thread 71 | 72 | ### v1.1.0 73 | 74 | - [editor] c+d open document 75 | - [project] detect external file changes 76 | - [search] toggle focus 77 | - [project] hide file or dir selection pane after selected 78 | - [project] create new project 79 | - [shortcut] open project viewer with c+s+y on Windows(remain c+y on Linux and MacOS) 80 | - [project] only show project name 81 | - [project] keep project-files state after refreshing 82 | 83 | ### v1.0.4 84 | 85 | - [project:fix] add existed project won't work 86 | - [project] remove non-existed project automatically 87 | 88 | ### v1.0.3 89 | 90 | - [editor:improve] all kinds editor now support LaTeX input 91 | - [project:fix] add project on cancel 92 | 93 | ### v1.0.2 94 | 95 | - [project:fix] open file 96 | 97 | ### v1.0.1 98 | 99 | - [project:fix] close add choices board after selected 100 | 101 | ### v1.0.0 102 | 103 | - [project] add/remove directory 104 | - [project] add/remove file 105 | - [vc] c+k will push commit editor to front 106 | - [project] ignore some useless files/directories 107 | - [vc:fix] handle AM/UU/MM prefix as changes 108 | - [project] refresh project files viewer when select a new project 109 | 110 | ### v0.5.0 111 | 112 | - [vc:fix] c+k can trigger repeated commit editor 113 | - [fix] press space in search bar won't get exception now 114 | - [shortcut] c+f search by selection automatically(if any) 115 | - [editor] fix "tab space when no backward sexp throws unexpected exception" 116 | - [vc] c+s+p commits pull 117 | - [vc] c+s+k commits push 118 | - [repl:migrate] cursor would be fixed to prompt start position 119 | - [repl:migrate] select executed history via up/down key 120 | 121 | > **Note** only work at prompt start position to prevent affect editing expression 122 | 123 | - [repl] support special symbol input in repl 124 | 125 | ### v0.4.0 126 | 127 | - [editor:migrate] special symbol input like: `\all`, `\->` 128 | - [vc] buttons to quick modify 129 | - select all 130 | - unselect all 131 | - clean up 132 | - [project] open file on editing in existed tab, else in new tab 133 | - [project] remove project button 134 | - [shortcut] c+y show/hide project files viewer 135 | - [editor] auto wrap the text over view range 136 | - [shortcut] c+m invoke project manager 137 | - [project] manager 138 | - when no project selected, ask to choose one to open project files viewer 139 | - by default hiding project files viewer 140 | - c+x cut line if no selection, else cut selection 141 | - [migrate] c+b/click jump to definition 142 | - [migrate] c+r rename refactoring 143 | 144 | > **Note** haven't support cross-file refactoring since this simply rebind "Rename Identifier" action in DrRacket 145 | 146 | - [migrate] c+e run REPL 147 | - [vc:migrate] c+k show commit editor 148 | - [migrate] the following pairs would complete and wrap selected text automatically 149 | - `()` 150 | - `[]` 151 | - `{}` 152 | - `""` 153 | - [migrate] c+; comment selected text(or that line if no selected text) if uncommented, uncomment if commented 154 | - o+backspace delete previous sexp 155 | - c+backspace delete whole line from current position 156 | 157 | ### v0.3.0 158 | 159 | - [editor] autocomplete invoke smart insertion 160 | - [vc] let changed files clickable, when clicked means ready to commit, else is not 161 | - [vc] remove ready/changes zone concept 162 | - [starter:fix] non-existed project would popup warning message and won't open IDE 163 | - [repl] switching expression from evaluated history 164 | - up select previous 165 | - down select next 166 | - enter evaluate, record history and reset selection status 167 | - smart insertion 168 | - prepare for advanced auto complete 169 | - c+enter complete the insertion 170 | - customizable tab action 171 | 172 | ### v0.2.1 173 | 174 | - c+w close last tab correctly 175 | - c+e improve eval module behavior 176 | - would run submodule test and main 177 | - work with project path, not path where user run executable **sauron** 178 | - allow GUI 179 | - change error output way 180 | 181 | ### v0.2.0 182 | 183 | - Editor Buffer 184 | - c+w would close current buffer 185 | - c+[0..9] would select tab 186 | - Project Files Panel 187 | - interactive with files 188 | - open file via double click 189 | - Version Control Panel 190 | - ready/changes zone 191 | - c+enter commit in commit message editor 192 | - c+k open version control panel 193 | - REPL switch on/off button 194 | - fix c+r, renaming broken in somewhere 195 | - fix c+e, now program evaluation result has output to REPL 196 | 197 | ### v0.1.0 198 | 199 | - c+o open file 200 | - c+s save file(would auto indent code) 201 | - c+e run REPL 202 | - c+a select all 203 | - c+c copy 204 | - c+v paste 205 | - c+z undo 206 | - c+x cut 207 | - c+up/down/left/right move to most up/down/left/right 208 | - o+left move left by a token/s-exp 209 | - o+right move right by a token/s-exp 210 | - c+b/click jump to definition(notice that only identifier is clickable to trigger this) 211 | - c+d open document 212 | - c+r rename all bound 213 | - c+; comment selected text or line if uncommented, uncomment if commented 214 | - c+f open text searcher 215 | - `(`/`[`/`{`/`"` when has selected text, wrap selected text automatically 216 | - Auto complete 217 | -------------------------------------------------------------------------------- /project/panel.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/gui 2 | #| 3 | NOTICE: modify from example in https://github.com/racket/gui/blob/master/gui-doc/mrlib/scribblings/hierlist/hierlist.scrbl based on MIT/APACHE2.0 4 | origin author: https://github.com/racket/gui/graphs/contributors 5 | modifier author: Lîm Tsú-thuàn(GitHub: @dannypsnl) 6 | |# 7 | (provide project-files-pane%) 8 | (require mrlib/hierlist 9 | file-watchers 10 | framework/preferences 11 | sauron/path/ignore 12 | sauron/path/util 13 | sauron/collect/api 14 | sauron/collect/record-maintainer 15 | sauron/project/dir-state 16 | sauron/path/renamer) 17 | 18 | (let ([cache-project-dir #f] 19 | [cache-project-watcher #f]) 20 | (preferences:add-callback 21 | 'current-project 22 | (λ (_ new-proj-dir) 23 | (when (path-string? new-proj-dir) 24 | (unless (equal? new-proj-dir cache-project-dir) 25 | ; stop project watcher if existed 26 | (when cache-project-watcher 27 | (kill-thread cache-project-watcher)) 28 | ; reset the project watcher 29 | (set! cache-project-watcher (robust-watch new-proj-dir)) 30 | ; start creating 31 | (start-tracking new-proj-dir ignore?) 32 | ; reset the project directory cache 33 | (set! cache-project-dir new-proj-dir))))) 34 | (void)) 35 | 36 | (define set-text-mixin 37 | (mixin (hierarchical-list-item<%>) ((interface () set-text get-text)) 38 | (inherit get-editor) 39 | (super-new) 40 | 41 | ; get-text: return the label of item 42 | (define/public (get-text) 43 | (define t (get-editor)) ; a text% object 44 | (send t get-text)) 45 | 46 | ; set-text: this sets the label of the item 47 | (define/public (set-text str) 48 | (define t (get-editor)) ; a text% object 49 | (send t erase) 50 | (send t insert str)))) 51 | 52 | (struct selected (dir file parent-dir) #:transparent) 53 | 54 | (define project-files% 55 | (class hierarchical-list% 56 | (init editor-panel) 57 | (define the-editor-panel editor-panel) 58 | (define table-path=>item (make-hash)) 59 | (define (path->key path) (build-path path "$$")) 60 | (define (get-item-by-path path) 61 | (hash-ref table-path=>item (path->key path))) 62 | (define (store-item-into-path path item) 63 | (hash-set! table-path=>item (path->key path) item)) 64 | (define (remove-item path) 65 | (define item (get-item-by-path path)) 66 | (define parent-item (send item get-parent)) 67 | (send parent-item delete-item item) 68 | (hash-remove! table-path=>item (path->key path))) 69 | 70 | ; new-item : create new item for a file or directory 71 | (define (new-item parent-dir directory subpath) 72 | (when (dir-open? directory) 73 | (send parent-dir open)) 74 | (define cur-path (build-path directory subpath)) 75 | (when (not (ignore? subpath)) 76 | (match (file-or-directory-type cur-path #t) 77 | ['file 78 | (define item (send parent-dir new-item set-text-mixin)) 79 | (send* item 80 | [set-text (path->string subpath)] 81 | [user-data (selected directory cur-path directory)]) 82 | (store-item-into-path cur-path item)] 83 | ['directory 84 | (define item (send parent-dir new-list set-text-mixin)) 85 | (send* item 86 | [set-text (path->string subpath)] 87 | [user-data (selected cur-path cur-path directory)]) 88 | (store-item-into-path cur-path item) 89 | (for ([subpath (directory-list cur-path)]) 90 | (new-item item cur-path subpath))] 91 | ['link (void)]))) 92 | 93 | (define/public (get-cur-selected-dir) (selected-dir current-selected)) 94 | (define/public (get-cur-selected-file) (selected-file current-selected)) 95 | (define/public (get-cur-selected-parent-dir) (selected-parent-dir current-selected)) 96 | 97 | (define/override (on-select i) (set! current-selected (send i user-data))) 98 | (define/override (on-double-select i) 99 | (define path (selected-file (send i user-data))) 100 | (when (file-exists? path) ;; when double-click a file, open it in editor 101 | (define tab (send the-editor-panel get-current-tab)) 102 | (let ([tab- (send the-editor-panel find-matching-tab path)]) 103 | (if tab- 104 | (send the-editor-panel change-to-tab tab-) 105 | (send the-editor-panel open-in-new-tab path))) 106 | (unless (send (send tab get-defs) get-filename) 107 | (send the-editor-panel close-given-tab tab)))) 108 | 109 | (define/override (on-item-opened i) 110 | (match-define (struct* selected ([dir dir])) (send i user-data)) 111 | (open-dir dir)) 112 | (define/override (on-item-closed i) 113 | (match-define (struct* selected ([dir dir])) (send i user-data)) 114 | (close-dir dir)) 115 | 116 | ;;; init 117 | (super-new) 118 | (define top-dir-list (send this new-list set-text-mixin)) 119 | (define current-selected #f) 120 | ;;; listener 121 | (thread (λ () 122 | (let loop () 123 | (match (file-watcher-channel-get) 124 | [(list 'robust 'add path) 125 | (when (not (ignore? path)) 126 | (create path) 127 | ;;; insert item 128 | (new-item (get-item-by-path (parent-path path)) 129 | (path-only path) 130 | (basepath path)) 131 | (send this sort (λ (lhs rhs) 132 | (string . 0) 78 | (define last-char (send ints get-character last-pos)) 79 | (when (char-whitespace? last-char) 80 | (send ints delete last-pos (+ last-pos 1)) 81 | (loop)))) 82 | 83 | ;; put back a single newline 84 | (send ints insert 85 | "\n" 86 | (send ints last-position) 87 | (send ints last-position)) 88 | 89 | ;; make sure the interactions is visible 90 | ;; and run the submitted expression 91 | (send frame ensure-rep-shown ints) 92 | (when shift-focus? (send (send ints get-canvas) focus)) 93 | (send ints do-submission))) 94 | 95 | ;;; c+r rename identifier 96 | (cmd/ctrl+ "r" (λ (editor event) (send-command "Rename Identifier" editor event))) 97 | ;;; c+s save file 98 | (cmd/ctrl+ "s" 99 | (λ (editor event) 100 | (when (object-method-arity-includes? editor 'set-needs-execution-message 1) 101 | (define filename (send editor get-filename)) 102 | (if filename 103 | ; if file exists 104 | (send editor save-file) 105 | ; else invoke the finder helper to store file 106 | (let ([project-dir (preferences:get 'current-project)]) 107 | (define filename (if project-dir 108 | (finder:put-file "Untitled" project-dir) 109 | (finder:put-file))) 110 | (send editor save-file filename) 111 | ))))) 112 | ;;; c+x cut line if no selection, else cut selection 113 | (cmd/ctrl+ "x" 114 | (λ (editor event) 115 | (let* ([s (send editor get-start-position)] 116 | [e (send editor get-end-position)] 117 | [select? (not (= s e))]) 118 | (unless select? 119 | (let* ([start-line (send editor position-line (send editor get-start-position))] 120 | [end-line (send editor position-line (send editor get-end-position))] 121 | [start (send editor line-start-position start-line)] 122 | [end (send editor line-end-position end-line)]) 123 | (send editor set-position start end))) 124 | (send-command "cut-clipboard" editor event)))) 125 | ;;; c+b 126 | ; 1. jump to definition (on a binding/reference) 127 | ; 2. show references of current definition (on a definition) 128 | (define (jump-to-def editor event) 129 | (jump-add! (send editor get-tab) (send editor get-start-position)) 130 | (define filename (send editor get-filename)) 131 | (define start-pos (send editor get-start-position)) 132 | (cond 133 | [(and filename (get-def filename start-pos)) 134 | (define id (get-def filename start-pos)) 135 | (show-references editor filename id)] 136 | [else 137 | (and 138 | (send-command "Jump to Definition (in Other File)" editor event) 139 | (send-command "Jump to Binding Occurrence" editor event))])) 140 | (cmd/ctrl+ "b" jump-to-def) 141 | (cmd/ctrl+ "leftbutton" jump-to-def) 142 | (cmd/ctrl+ "s:b" 143 | (λ (editor event) 144 | (match (jump-pop!) 145 | [#f (void)] 146 | [(jump-pos tab pos) 147 | (define frame (send (send editor get-tab) get-frame)) 148 | (send frame change-to-tab tab) 149 | (define ed (send tab get-defs)) 150 | (send ed set-position pos)]))) 151 | 152 | ;;; c+s+t reopen the recently closed tab 153 | (cmd/ctrl+ "s:t" 154 | (λ (editor event) 155 | (send+ editor 156 | (get-tab) 157 | (get-frame) 158 | (reopen-closed-tab)))) 159 | 160 | ;;; delete whole thing from current position to the start of line 161 | (cmd/ctrl+ "backspace" 162 | (λ (editor event) 163 | (define end (send editor get-start-position)) 164 | (define line (send editor position-line end)) 165 | (define start (send editor line-start-position line)) 166 | (send editor delete start end))) 167 | 168 | ;;; delete previous sexp 169 | (opt/alt+ 170 | "backspace" 171 | (λ (editor event) 172 | (if (object-method-arity-includes? editor 'get-backward-sexp 1) 173 | (let* ([cur-pos (send editor get-start-position)] 174 | [pre-sexp-pos (send editor get-backward-sexp cur-pos)]) 175 | ; ensure pre-sexp existed 176 | (when pre-sexp-pos 177 | (send editor delete pre-sexp-pos cur-pos))) 178 | (send (if (object-method-arity-includes? editor 'get-editor 0) (send editor get-editor) editor) 179 | delete 180 | 'start)))) 181 | 182 | ;;; comment/uncomment selected text, if no selected text, target is current line 183 | (cmd/ctrl+ 184 | "semicolon" 185 | (λ (editor event) 186 | ; NOTE: get-start-position and get-end-position would have same value when no selected text 187 | ; following code comment all lines of selected text(or automatically select cursor line) 188 | (let* ([start-line (send editor position-line (send editor get-start-position))] 189 | [end-line (send editor position-line (send editor get-end-position))] 190 | [start (send editor line-start-position start-line)] 191 | [end (send editor line-end-position end-line)] 192 | [selected-text (send editor get-text start end)]) 193 | (if (string-contains? selected-text ";") 194 | (send editor uncomment-selection start end) 195 | (send editor comment-out-selection start end)) 196 | (send editor set-position start)))) 197 | 198 | (define vc-open? #f) 199 | (define frame- #f) 200 | (cmd/ctrl+ "k" 201 | (λ (editor event) 202 | (define vc-frame% 203 | (class frame% 204 | (super-new [label "Version Control: Commit"] [width 300] [height 600]) 205 | 206 | (define/augment (on-close) (set! vc-open? #f)))) 207 | (unless vc-open? 208 | (set! vc-open? #t) 209 | (set! frame- (new vc-frame%)) 210 | (new version-control% [parent frame-])) 211 | (when frame- 212 | (send* frame- [center] [show #t])))) 213 | (cmd/ctrl+ "s:k" (λ (editor event) (make-pusher "push"))) 214 | (cmd/ctrl+ "s:p" (λ (editor event) (make-pusher "pull"))) 215 | 216 | (cmd/ctrl+ "m" 217 | (λ (editor event) 218 | (define manager 219 | (new project-manager% 220 | [label "select a project"] 221 | [on-select (λ (path) (preferences:set 'current-project path))])) 222 | (send manager run))) 223 | 224 | (cmd/ctrl+ "d" 225 | (λ (editor event) 226 | (define filename- (send editor get-filename)) 227 | (when filename- 228 | ;;; FIXME: this should also works for untitled file 229 | (define doc-page- (get-doc filename- (send editor get-start-position))) 230 | (when doc-page- 231 | (send-url doc-page- #f))))) 232 | 233 | (keybinding "(" (λ (editor event) (send-command "insert-()-pair" editor event))) 234 | (keybinding "[" (λ (editor event) (send-command "insert-[]-pair" editor event))) 235 | (keybinding "{" (λ (editor event) (send-command "insert-{}-pair" editor event))) 236 | (keybinding "\"" (λ (editor event) (send-command "insert-\"\"-pair" editor event))) 237 | 238 | (keybinding 239 | "space" 240 | (λ (editor event) 241 | (when (object-method-arity-includes? editor 'get-backward-sexp 1) 242 | (define end (send editor get-start-position)) 243 | (define start (send editor get-backward-sexp end)) 244 | (when start 245 | (define to-complete (send editor get-text start end)) 246 | (when (string-prefix? to-complete "\\") 247 | ;;; select previous sexp 248 | (send editor set-position start end) 249 | ;;; replace it with new text 250 | (send editor 251 | insert 252 | (hash-ref latex-complete (string-trim to-complete "\\" #:right? #f) to-complete))))) 253 | (send (if (object-method-arity-includes? editor 'get-editor 0) (send editor get-editor) editor) 254 | insert 255 | " "))) 256 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------