├── .gitignore ├── demo.gif ├── test ├── css.css ├── lua.lua ├── .zshrc ├── clojure.clj ├── sh.sh ├── go.go ├── nim.nim ├── handlebars.hbs.html ├── typescript.ts ├── nix.nix ├── clojure.bb ├── haskell.hs ├── javascript.js ├── javascriptreact.jsx ├── scss.scss ├── rust.rs ├── c.c ├── typescriptreact.tsx ├── php.php ├── scala.scala ├── elixir.exs ├── svelte.svelte ├── python.py ├── html.html ├── vue.vue ├── clojure.cljc ├── r.r ├── yaml.yaml └── erlang.erl ├── .github ├── ISSUE_TEMPLATE │ ├── support_draft.md │ └── bug_report.md └── workflows │ └── release.yml ├── lua └── comment-hide │ ├── init.lua │ ├── commands.lua │ └── utils.lua ├── LICENSE ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .annotations 2 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/my-rubbish/nvim.comment-hide/HEAD/demo.gif -------------------------------------------------------------------------------- /test/css.css: -------------------------------------------------------------------------------- 1 | body { 2 | /* multi line comment with // style test */ 3 | /* 4 | // This test 5 | // Test 6 | */ 7 | color: red; /* multi line comment with */ 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support_draft.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Support draft' 3 | about: Support your like lang. 4 | title: 'Support: ' 5 | labels: 'enhancement' 6 | --- 7 | 8 | ## Support lang 9 | 10 | [//]: # '[ex: Javascript]' 11 | 12 | ## Are you willing to finish it? 13 | 14 | - [x] : **YES! (YOU ARE SO GOOD!)** 15 | -------------------------------------------------------------------------------- /lua/comment-hide/init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local default_config = { 4 | gitignore = false 5 | } 6 | 7 | M.config = nil 8 | 9 | local function setup(opts) 10 | M.config = vim.tbl_deep_extend("force", {}, default_config, opts or {}) 11 | require("comment-hide.commands").setup(M.config) 12 | end 13 | 14 | M.setup = setup 15 | 16 | return M 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Bug Report' 3 | about: Report a reproducible bug. 4 | title: 'Bug Lang: ' 5 | labels: 'Status: Unconfirmed' 6 | --- 7 | 8 | ## Expected Behavior 9 | 10 | [//]: # '[Describe what you expected to happen.]' 11 | 12 | ## Actual Behavior 13 | 14 | [//]: # '[Describe what actually happened.]' 15 | 16 | ## Environment 17 | 18 | [//]: # '- Neovim version:' 19 | 20 | ## Possible Solution 21 | 22 | [//]: # '[If you have any ideas on how to fix or address the issue, please share them here.]' 23 | 24 | - [x] : **Do you fix this error?(YOU ARE SO GOOD!)** 25 | -------------------------------------------------------------------------------- /test/lua.lua: -------------------------------------------------------------------------------- 1 | --[[ return { ]] 2 | --[[ { ]] 3 | --[[ dir = "/Users/rhyme/Code/project/Jiangxue/nvim.comment-hide", ]] 4 | --[[ name = "comment-hide", ]] 5 | --[[ dev = true, ]] 6 | --[[ config = function() ]] 7 | --[[ require("comment-hide").setup() ]] 8 | --[[ end, ]] 9 | --[[ } ]] 10 | --[[ } ]] 11 | 12 | return { 13 | --[[ "jiangxue-analysis/nvim.comment-hide", ]] 14 | dir = "/users/uwu/Code/project/jiangxue/nvim.comment-hide", 15 | name = "comment-hide", 16 | lazy = false, 17 | config = function() 18 | require("comment-hide").setup({ 19 | gitignore = true, 20 | }) 21 | vim.keymap.set("n", "vs", "CommentHideSave", { desc = "Comment: Save (strip comments)" }) 22 | vim.keymap.set("n", "vr", "CommentHideRestore", { desc = "Comment: Restore from backup" }) 23 | end, 24 | } 25 | -------------------------------------------------------------------------------- /test/.zshrc: -------------------------------------------------------------------------------- 1 | cds() { 2 | local raw target_path commands cmds_part 3 | 4 | raw=$(command cds "$@" 2>&1 1>/dev/tty) 5 | 6 | if [[ "$raw" == CDS_RESULT:* ]]; then 7 | raw="${raw#CDS_RESULT:}" 8 | if [[ "$raw" == *"|CDS_COMMANDS|"* ]]; then 9 | target_path=${raw%%"|CDS_COMMANDS|"*} 10 | cmds_part=${raw#*"|CDS_COMMANDS|"} 11 | commands=$(printf '%s' "$cmds_part" | sed 's/|CDS_SEP|/;/g') 12 | else 13 | target_path="$raw" 14 | commands="" 15 | fi 16 | 17 | target_path=$(printf '%s' "$target_path" | tr -d '\r') 18 | builtin cd "$target_path" 19 | 20 | if [[ -n "$commands" ]]; then 21 | IFS=';' read -rA cmds <<< "$commands" 22 | for cmd in "${cmds[@]}"; do 23 | [[ -n "$cmd" ]] && { 24 | echo "Executing: $cmd" 25 | [[ "$cmd" == "nix-shell" ]] && exec $cmd || eval "$cmd" 26 | } 27 | done 28 | fi 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /test/clojure.clj: -------------------------------------------------------------------------------- 1 | #! bb 2 | 3 | (def chars ["\uee06" "\uee07" "\uee08" "\uee09" "\uee0a" "\uee0b"]) 4 | 5 | (loop [i 0] 6 | (print 7 | (str 8 | ; \u001b \u000d " " ;; ESC CR Moves the cursor to column zero 9 | \u001b "[2J" 10 | \u001b "[H" 11 | 12 | ;; normal 13 | "\n Other fonts [" 14 | (str/join 15 | (for [j (range 2 17)] 16 | (if (<= j i) "#" "."))) 17 | "] " 18 | (nth "|/-\\" (mod i 4)) 19 | " " 20 | 21 | ;; Fira Code 22 | "\n\n Fira Code " 23 | (if (= 0 i) \uee00 \uee03) ;; Progress start 24 | (str/join 25 | (for [j (range 2 17)] 26 | (if (<= j i) \uee04 \uee01))) 27 | (if (= 17 i) \uee05 \uee02) 28 | " " 29 | (nth chars (mod i 6)) 30 | " " 31 | #_#_(-> i (/ 17.0) (* 100) (int)) "%" 32 | " ")) 33 | (flush) 34 | (Thread/sleep 200) 35 | (recur (mod (inc i) 18))) 36 | -------------------------------------------------------------------------------- /test/sh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #-*- sh -*- 3 | 4 | # internal vars dont touch # 5 | ARGS=( $@ ) 6 | NARG=0 7 | PROG_NAME=$0 8 | ############################ 9 | 10 | # $1 offset 11 | getarg() { 12 | echo ${ARGS[$NARG + $1]} 13 | } 14 | 15 | argv() { 16 | R=${ARGS[@]:$NARG} 17 | echo ${R[@]} 18 | } 19 | 20 | target_build() { 21 | make all 22 | } 23 | 24 | target_run() { 25 | target_build 26 | bin/printf2_$1 `argv` 27 | } 28 | 29 | target_test() { 30 | make test/$1.test.out && test/$1.test.out 31 | rm -f test/$1.test.out 32 | } 33 | 34 | hasargs=1 35 | 36 | # Fetch all sparse args 37 | while [ $hasargs -eq 1 ] 38 | do 39 | case `getarg 0` in 40 | *) 41 | hasargs=0 42 | ;; 43 | esac 44 | done 45 | 46 | case `getarg 0` in 47 | package) 48 | make package 49 | ;; 50 | test) 51 | target_test `getarg 1` 52 | ;; 53 | build) 54 | target_build 55 | ;; 56 | run) 57 | target_run `getarg 1` 58 | ;; 59 | *) 60 | echo "You can do: run, build" 61 | ;; 62 | esac 63 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: Release 6 | jobs: 7 | release-please: 8 | name: Release Please 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: GoogleCloudPlatform/release-please-action@v3 13 | id: release 14 | with: 15 | token: ${{ secrets.GITHUB_TOKEN }} 16 | release-type: node 17 | package-name: standard-version 18 | changelog-types: '[{"type": "types", "section":"Types", "hidden": false},{"type": "revert", "section":"Reverts", "hidden": false},{"type": "feat", "section": "Features", "hidden": false},{"type": "fix", "section": "Bug Fixes", "hidden": false},{"type": "docs", "section":"Docs", "hidden": false},{"type": "style", "section":"Styling", "hidden": false},{"type": "refactor", "section":"Code Refactoring", "hidden": false},{"type": "perf", "section":"Performance Improvements", "hidden": false},{"type": "test", "section":"Tests", "hidden": false},{"type": "build", "section":"Build System", "hidden": false},{"type": "ci", "section":"CI", "hidden":false}]' 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 jiangxue-analysis 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 | -------------------------------------------------------------------------------- /test/go.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 The Syncthing Authors. 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 | // You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | package build 8 | 9 | import ( 10 | "errors" 11 | "regexp" 12 | "strings" 13 | ) 14 | 15 | // syncthing v1.1.4-rc.1+30-g6aaae618-dirty-crashrep "Erbium Earthworm" (go1.12.5 darwin-amd64) jb@kvin.kastelo.net 2019-05-23 16:08:14 UTC [foo, bar] 16 | // or, somewhere along the way the "+" in the version tag disappeared: 17 | // syncthing v1.23.7-dev.26.gdf7b56ae.dirty-stversionextra "Fermium Flea" (go1.20.5 darwin-arm64) jb@ok.kastelo.net 2023-07-12 06:55:26 UTC [Some Wrapper, purego, stnoupgrade] 18 | var ( 19 | longVersionRE = regexp.MustCompile(`syncthing\s+(v[^\s]+)\s+"([^"]+)"\s\(([^\s]+)\s+([^-]+)-([^)]+)\)\s+([^\s]+)[^\[]*(?:\[(.+)\])?$`) 20 | gitExtraRE = regexp.MustCompile(`\.\d+\.g[0-9a-f]+`) // ".1.g6aaae618" 21 | gitExtraSepRE = regexp.MustCompile(`[.-]`) // dot or dash 22 | ) 23 | 24 | type VersionParts struct { 25 | Version string // "v1.1.4-rc.1+30-g6aaae618-dirty-crashrep" 26 | Tag string // "v1.1.4-rc.1" 27 | Commit string // "6aaae618", blank when absent 28 | Codename string // "Erbium Earthworm" 29 | Runtime string // "go1.12.5" 30 | GOOS string // "darwin" 31 | GOARCH string // "amd64" 32 | Builder string // "jb@kvin.kastelo.net" 33 | Extra []string // "foo", "bar" 34 | } 35 | 36 | func (v VersionParts) Environment() string { 37 | if v.Commit != "" { 38 | return "Development" 39 | } 40 | if strings.Contains(v.Tag, "-rc.") { 41 | return "Candidate" 42 | } 43 | if strings.Contains(v.Tag, "-") { 44 | return "Beta" 45 | } 46 | return "Stable" 47 | } 48 | 49 | func ParseVersion(line string) (VersionParts, error) { 50 | m := longVersionRE.FindStringSubmatch(line) 51 | if len(m) == 0 { 52 | return VersionParts{}, errors.New("unintelligeble version string") 53 | } 54 | 55 | v := VersionParts{ 56 | Version: m[1], 57 | Codename: m[2], 58 | Runtime: m[3], 59 | GOOS: m[4], 60 | GOARCH: m[5], 61 | Builder: m[6], 62 | } 63 | 64 | // Split the version tag into tag and commit. This is old style 65 | // v1.2.3-something.4+11-g12345678 or newer with just dots 66 | // v1.2.3-something.4.11.g12345678 or v1.2.3-dev.11.g12345678. 67 | parts := []string{v.Version} 68 | if strings.Contains(v.Version, "+") { 69 | parts = strings.Split(v.Version, "+") 70 | } else { 71 | idxs := gitExtraRE.FindStringIndex(v.Version) 72 | if len(idxs) > 0 { 73 | parts = []string{v.Version[:idxs[0]], v.Version[idxs[0]+1:]} 74 | } 75 | } 76 | v.Tag = parts[0] 77 | if len(parts) > 1 { 78 | fields := gitExtraSepRE.Split(parts[1], -1) 79 | if len(fields) >= 2 && strings.HasPrefix(fields[1], "g") { 80 | v.Commit = fields[1][1:] 81 | } 82 | } 83 | 84 | if len(m) >= 8 && m[7] != "" { 85 | tags := strings.Split(m[7], ",") 86 | for i := range tags { 87 | tags[i] = strings.TrimSpace(tags[i]) 88 | } 89 | v.Extra = tags 90 | } 91 | 92 | return v, nil 93 | } 94 | -------------------------------------------------------------------------------- /test/nim.nim: -------------------------------------------------------------------------------- 1 | # Arraymancer compilation flag config 2 | 3 | @if cudnn: 4 | define:"cuda" 5 | @end 6 | 7 | # Nim cfg is not aware of new define within the file: https://github.com/nim-lang/Nim/issues/6698 8 | 9 | @if cuda or cudnn: 10 | # compile with "cpp" backend. 11 | # See https://github.com/mratsim/Arraymancer/issues/371 12 | 13 | # Nvidia NVCC 14 | cincludes:"/opt/cuda/include" 15 | cc:"clang" 16 | # Compilation for Cuda requires C++ 17 | # Nim also uses -std=gnu++14 for GCC which NVCC doesn't support 18 | # so use Clang (NVCC is LLVM based now anyway) 19 | clang.cpp.exe:"/opt/cuda/bin/nvcc" 20 | clang.cpp.linkerexe:"/opt/cuda/bin/nvcc" 21 | clang.cpp.options.debug: "-Xcompiler -Og" # Additional "-Xcompiler -g3" crashes stb_image macros 22 | clang.cpp.options.speed: "-Xcompiler -O3 -Xcompiler -fno-strict-aliasing" 23 | clang.cpp.options.size: "-Xcompiler -Os" 24 | # Important sm_61 architecture corresponds to Pascal and sm_75 to Turing. Change for your own card 25 | clang.cpp.options.always:"-gencode arch=compute_61,code=sm_61 -gencode arch=compute_75,code=sm_75 --x cu -Xcompiler -fpermissive" 26 | 27 | # Clang 28 | # cincludes:"/opt/cuda/include" 29 | # clibdir:"/opt/cuda/lib" 30 | # cc:"clang" 31 | # # Compile for Pascal (6.1) and Turing cards (7.5) 32 | # clang.cpp.options.always:"--cuda-path=/opt/cuda -lcudart_static -x cuda --cuda-gpu-arch=sm_61 --cuda-gpu-arch=sm_75" 33 | 34 | @end 35 | 36 | @if openblas: 37 | define:"blas=openblas" # For nimblas 38 | define:"lapack=openblas" # For nimlapack 39 | @if macosx: 40 | clibdir:"/usr/local/opt/openblas/lib" 41 | cincludes:"/usr/local/opt/openblas/include" 42 | @end 43 | @end 44 | 45 | # blis # Completely managed in the blis backend code 46 | 47 | # native # -march=native flag is Handled in the code 48 | 49 | @if mkl: # MKL multi_threaded 50 | define:"openmp" 51 | define:"blas=mkl_intel_lp64" 52 | define:"lapack=mkl_intel_lp64" 53 | clibdir:"/opt/intel/mkl/lib/intel64" 54 | passl:"/opt/intel/mkl/lib/intel64/libmkl_intel_lp64.a" 55 | passl:"-lmkl_core" 56 | passl:"-lmkl_gnu_thread" 57 | passl:"-lgomp" 58 | dynlibOverride:"mkl_intel_lp64" 59 | @end 60 | 61 | @if openmp or mkl: 62 | stackTrace:off 63 | @if macosx: # Default compiler on Mac is clang without OpenMP and gcc is an alias to clang. 64 | # Use Homebrew GCC instead for OpenMP support. GCC (v7), must be properly linked via `brew link gcc` 65 | cc:"gcc" 66 | gcc.exe:"/usr/local/bin/gcc-7" 67 | gcc.linkerexe:"/usr/local/bin/gcc-7" 68 | @end 69 | @end 70 | 71 | # ############################################################ 72 | # 73 | # SIMD flags 74 | # 75 | # ############################################################ 76 | 77 | gemm_ukernel_sse.always = "-msse" 78 | gemm_ukernel_sse2.always = "-msse2" 79 | gemm_ukernel_sse4_1.always = "-msse4.1" 80 | gemm_ukernel_avx.always = "-mavx" 81 | gemm_ukernel_avx_fma.always = "-mavx -mfma" 82 | gemm_ukernel_avx2.always = "-mavx2" 83 | gemm_ukernel_avx512.always = "-mavx512f -mavx512dq" 84 | 85 | 86 | # Set project wide style check to "usages" so that we are only hinted / errored 87 | # for style that mismatches with our initial usage of variables / proc names. 88 | styleCheck:"usages" 89 | -------------------------------------------------------------------------------- /test/handlebars.hbs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{#if is_landing}} 7 | {{fluent "index-title"}} 8 | {{else}} 9 | {{#fluent "page-title"}}{{#fluentparam "pagename"}}{{title}}{{/fluentparam}}{{/fluent}} 10 | {{/if}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{#if is_landing}} 48 | 49 | {{#each locales as |locale| ~}} 50 | 51 | {{/each~}} 52 | 53 | {{/if}} 54 | 55 | 57 | 58 | 59 | 60 | 61 | {{> components/nav}} 62 |
63 | {{~> page}} 64 |
65 | {{> components/footer}} 66 | {{#if pontoon_enabled}} 67 | 68 | {{/if}} 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /test/typescript.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/zeit/ms/blob/master/index.js 2 | // Copyright (c) 2016 Zeit, Inc. MIT License 3 | // Copyright (c) 2018 Kevin "Kun" Kassimo Qian. MIT License 4 | 5 | const s = 1000; 6 | const m = s * 60; 7 | const h = m * 60; 8 | const d = h * 24; 9 | const w = d * 7; 10 | const y = d * 365.25; 11 | 12 | /** Parse or format the given `val`. 13 | * 14 | * Options: 15 | * 16 | * - `long` verbose formatting [false] 17 | */ 18 | 19 | export function ms( 20 | val: string | number, 21 | options?: { [key: string]: any }, 22 | ): string | number | undefined { 23 | switch (typeof val) { 24 | case "string": 25 | if ((val).length > 0) { 26 | return parse(val); 27 | } 28 | break; 29 | case "number": 30 | if (!isNaN(val)) { 31 | return options && options!.long ? fmtLong(val) : fmtShort(val); 32 | } 33 | } 34 | throw new Error( 35 | "val is not a non-empty string or a valid number. val=" + 36 | JSON.stringify(val), 37 | ); 38 | } 39 | 40 | /** Parse the given `str` and return milliseconds. 41 | */ 42 | 43 | function parse(str: string): number | undefined { 44 | if (str.length > 100) { 45 | return; 46 | } 47 | const match = 48 | /^((?:\d+)?-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i 49 | .exec( 50 | str, 51 | ); 52 | if (!match) { 53 | return; 54 | } 55 | const n = parseFloat(match[1]); 56 | const type = (match[2] || "ms").toLowerCase(); 57 | switch (type) { 58 | case "years": 59 | case "year": 60 | case "yrs": 61 | case "yr": 62 | case "y": 63 | return n * y; 64 | case "weeks": 65 | case "week": 66 | case "w": 67 | return n * w; 68 | case "days": 69 | case "day": 70 | case "d": 71 | return n * d; 72 | case "hours": 73 | case "hour": 74 | case "hrs": 75 | case "hr": 76 | case "h": 77 | return n * h; 78 | case "minutes": 79 | case "minute": 80 | case "mins": 81 | case "min": 82 | case "m": 83 | return n * m; 84 | case "seconds": 85 | case "second": 86 | case "secs": 87 | case "sec": 88 | case "s": 89 | return n * s; 90 | case "milliseconds": 91 | case "millisecond": 92 | case "msecs": 93 | case "msec": 94 | case "ms": 95 | return n; 96 | default: 97 | return undefined; 98 | } 99 | } 100 | 101 | /** Short format for `ms`. 102 | */ 103 | 104 | function fmtShort(ms: number): string { 105 | const msAbs = Math.abs(ms); 106 | if (msAbs >= d) { 107 | return Math.round(ms / d) + "d"; 108 | } 109 | if (msAbs >= h) { 110 | return Math.round(ms / h) + "h"; 111 | } 112 | if (msAbs >= m) { 113 | return Math.round(ms / m) + "m"; 114 | } 115 | if (msAbs >= s) { 116 | return Math.round(ms / s) + "s"; 117 | } 118 | return ms + "ms"; 119 | } 120 | 121 | /** Long format for `ms`. 122 | */ 123 | 124 | function fmtLong(ms: number): string { 125 | const msAbs = Math.abs(ms); 126 | if (msAbs >= d) { 127 | return plural(ms, msAbs, d, "day"); 128 | } 129 | if (msAbs >= h) { 130 | return plural(ms, msAbs, h, "hour"); 131 | } 132 | if (msAbs >= m) { 133 | return plural(ms, msAbs, m, "minute"); 134 | } 135 | if (msAbs >= s) { 136 | return plural(ms, msAbs, s, "second"); 137 | } 138 | return ms + " ms"; 139 | } 140 | 141 | /** Pluralization helper. 142 | */ 143 | 144 | function plural(ms: number, msAbs: number, n: number, name: string) { 145 | const isPlural = msAbs >= n * 1.5; 146 | return Math.round(ms / n) + " " + name + (isPlural ? "s" : ""); 147 | } 148 | -------------------------------------------------------------------------------- /test/nix.nix: -------------------------------------------------------------------------------- 1 | # Define inputs: testers framework and self-reference (flake self) 2 | { testers, self }: 3 | 4 | # Create a NixOS virtual machine test 5 | testers.nixosTest { 6 | name = "vm-test"; # Name of the test 7 | 8 | # Define the nodes for the test (in this case, just a server) 9 | nodes.server = { pkgs, ... }: { 10 | 11 | # Environment configuration 12 | environment = { 13 | # Files to be placed in /etc 14 | etc = { 15 | # Script that runs netcat to serve HTTP responses 16 | "nc.sh" = { 17 | text = '' 18 | #!/bin/sh 19 | while true; do 20 | ${pkgs.netcat}/bin/nc -l "$PORT" <<'EOF' 21 | HTTP/1.1 200 OK 22 | Content-Length: 13 23 | Connection: close 24 | 25 | Hello, world! 26 | EOF 27 | done 28 | ''; 29 | mode = "0755"; # Make the script executable 30 | }; 31 | 32 | # Keter configuration file 33 | "keter.yaml".text = '' 34 | stanzas: 35 | - type: webapp 36 | exec: ../nc.sh # Reference to our netcat script 37 | hosts: 38 | - localhost 39 | ''; 40 | 41 | # Deployment script to bundle and deploy the application 42 | "deploy.sh" = { 43 | text = '' 44 | mkdir -p /tmp/bundle/config 45 | cp /etc/nc.sh /tmp/bundle/nc.sh 46 | cp /etc/keter.yaml /tmp/bundle/config/keter.yaml 47 | tar -C /tmp/bundle -czvf /tmp/nc.keter . 48 | cp /tmp/nc.keter /opt/keter/incoming 49 | ''; 50 | mode = "0755"; # Make the script executable 51 | }; 52 | }; 53 | 54 | # Packages to be available system-wide 55 | systemPackages = [ pkgs.netcat ]; 56 | }; 57 | 58 | # Import additional NixOS modules 59 | imports = [ 60 | ./keter.nix # Import Keter service configuration 61 | ]; 62 | 63 | # Nix configuration settings 64 | nix.settings = { 65 | experimental-features = [ "nix-command" "flakes" ]; 66 | auto-optimise-store = true; 67 | }; 68 | 69 | # Keter service configuration 70 | services.keter-ng = { 71 | enable = true; # Enable the Keter service 72 | package = self.packages.x86_64-linux.keter; # Use Keter from our flake 73 | globalKeterConfig = '' 74 | root: /opt/keter 75 | rotate-logs: true 76 | listeners: 77 | - host: "*4" # Listen on all IPv4 interfaces 78 | port: 80 # Standard HTTP port 79 | ''; 80 | }; 81 | }; 82 | 83 | # Test script that runs in the virtual machine 84 | testScript = '' 85 | # Start the server node 86 | server.start() 87 | 88 | # Wait for Keter service to start 89 | server.wait_for_unit("keter-ng.service") 90 | 91 | # Wait for port 80 to be open 92 | server.wait_for_open_port(80) 93 | 94 | # Execute the deployment script 95 | server.succeed(". /etc/deploy.sh") 96 | 97 | # Wait for deployment to complete 98 | server.sleep(10) 99 | 100 | # Verify the web application responds correctly 101 | server.succeed("curl localhost | grep 'Hello, world!'") 102 | 103 | # Check for absence of file lock errors in logs 104 | server.succeed("! grep -q 'file is locked' /opt/keter/log/keter.log") 105 | 106 | # Test file watching functionality 107 | server.succeed("touch /opt/keter/incoming/nc.keter") 108 | server.sleep(10) 109 | 110 | # Verify no file lock errors occurred after touching the file 111 | server.succeed("! grep -q 'file is locked' /opt/keter/log/keter.log") 112 | ''; 113 | } 114 | -------------------------------------------------------------------------------- /lua/comment-hide/commands.lua: -------------------------------------------------------------------------------- 1 | local utils = require("comment-hide.utils") 2 | 3 | local M = {} 4 | 5 | local function get_project_root() 6 | local current_file = vim.api.nvim_buf_get_name(0) 7 | local project_root = vim.fn.finddir(".git", vim.fn.fnamemodify(current_file, ":p:h") .. ";") 8 | return project_root ~= "" and vim.fn.fnamemodify(project_root, ":h") or vim.fn.getcwd() 9 | end 10 | 11 | local function get_comment_store_path() 12 | return get_project_root() .. "/.annotations" 13 | end 14 | 15 | local function ensure_in_gitignore() 16 | if not M.config or not M.config.gitignore then 17 | return 18 | end 19 | 20 | local gitignore_path = get_project_root() .. "/.gitignore" 21 | local target_entry = ".annotations" 22 | 23 | if vim.fn.filereadable(gitignore_path) == 1 then 24 | for _, line in ipairs(vim.fn.readfile(gitignore_path)) do 25 | if line:match("^%s*" .. vim.pesc(target_entry) .. "%s*$") then 26 | return 27 | end 28 | end 29 | end 30 | 31 | local file = io.open(gitignore_path, "a") 32 | if file then 33 | file:write((vim.fn.getfsize(gitignore_path) > 0 and "\n" or "") .. target_entry .. "\n") 34 | file:close() 35 | vim.notify("Added '" .. target_entry .. "' to .gitignore", vim.log.levels.INFO) 36 | else 37 | vim.notify("Failed to update .gitignore file", vim.log.levels.WARN) 38 | end 39 | end 40 | 41 | local function save_comments() 42 | local buf = vim.api.nvim_get_current_buf() 43 | local filepath = vim.api.nvim_buf_get_name(buf) 44 | local project_root = get_project_root() 45 | 46 | local comment_store_path = get_comment_store_path() 47 | local relative_path = filepath:gsub(project_root .. "/", "") 48 | 49 | if vim.fn.isdirectory(comment_store_path) == 0 then 50 | vim.fn.mkdir(comment_store_path, "p") 51 | end 52 | 53 | local content = table.concat(vim.api.nvim_buf_get_lines(buf, 0, -1, false), "\n") 54 | local filetype = vim.api.nvim_buf_get_option(buf, "filetype") 55 | 56 | local comments, uncommented_code = utils.extract_comments(content, filetype) 57 | 58 | local comment_file_path = comment_store_path .. "/" .. relative_path .. ".json" 59 | vim.fn.mkdir(vim.fn.fnamemodify(comment_file_path, ":h"), "p") 60 | 61 | local comment_data = { 62 | originalContent = content, 63 | comments = comments, 64 | filePath = relative_path, 65 | timestamp = os.date("%Y-%m-%d %H:%M:%S"), 66 | } 67 | 68 | vim.fn.writefile({ vim.json.encode(comment_data) }, comment_file_path) 69 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(uncommented_code, "\n")) 70 | 71 | vim.notify("Comments removed. Backup at: " .. comment_file_path, vim.log.levels.INFO) 72 | end 73 | 74 | local function restore_comments() 75 | local buf = vim.api.nvim_get_current_buf() 76 | local filepath = vim.api.nvim_buf_get_name(buf) 77 | local project_root = get_project_root() 78 | 79 | local comment_store_path = get_comment_store_path() 80 | local relative_path = filepath:gsub(project_root .. "/", "") 81 | local comment_file_path = comment_store_path .. "/" .. relative_path .. ".json" 82 | 83 | if vim.fn.filereadable(comment_file_path) == 0 then 84 | vim.notify("No saved comments found at: " .. comment_file_path, vim.log.levels.ERROR) 85 | return 86 | end 87 | 88 | local ok, comment_data = pcall(vim.json.decode, table.concat(vim.fn.readfile(comment_file_path), "\n")) 89 | if not ok then 90 | vim.notify("Failed to parse comment file", vim.log.levels.ERROR) 91 | return 92 | end 93 | 94 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(comment_data.originalContent, "\n")) 95 | vim.notify("Restored comments from: " .. comment_file_path, vim.log.levels.INFO) 96 | end 97 | 98 | function M.setup(opts) 99 | if M.config then return end 100 | 101 | M.config = vim.tbl_deep_extend("force", { 102 | gitignore = false, 103 | }, opts or {}) 104 | 105 | vim.api.nvim_create_user_command("CommentHideSave", function() 106 | if M.config.gitignore then 107 | ensure_in_gitignore() 108 | end 109 | save_comments() 110 | end, { desc = "Save and strip comments" }) 111 | 112 | vim.api.nvim_create_user_command("CommentHideRestore", restore_comments, { 113 | desc = "Restore original content with comments", 114 | }) 115 | end 116 | 117 | return M 118 | -------------------------------------------------------------------------------- /test/clojure.bb: -------------------------------------------------------------------------------- 1 | ;; -*- mode: clojure -*- 2 | 3 | ;; You may want to set an alias for mage: 4 | ;; alias mage='cd ~/your/repo/metabase && ./bin/mage' 5 | 6 | (ns mage 7 | (:require 8 | [babashka.tasks :as bt] 9 | [clojure.string :as str] 10 | [mage.color :as c] 11 | [mage.util :as u])) 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | (defn- lolcat [f] 16 | (if (try (u/sh "command -v lolcat") 17 | (catch Exception _ false)) 18 | (bt/shell (str "lolcat " f)) 19 | (do 20 | (println (slurp f)) 21 | (when (> (rand) 0.8) 22 | (println (c/yellow "Tip: install lolcat for a better experience!")))))) 23 | 24 | (defn- invalid-task? [] 25 | (let [task-name (first *command-line-args*)] 26 | (when-not (contains? (set (u/all-bb-tasks-list)) task-name) 27 | (println (c/red (str "Unknown task: " task-name))) 28 | true))) 29 | 30 | (defn- zen [] 31 | (mapv (comp #(str "Zen of Metabase: " %) #(apply str %) #(drop 2 %)) 32 | (filter (fn [x] (str/starts-with? x "-")) 33 | (str/split-lines (slurp "zen.md"))))) 34 | 35 | (defn- tip-o-day [] 36 | (rand-nth 37 | (concat ["Did you know? You can use `./bin/mage --help` to get more information about a specific task." 38 | "Pro tip: Use `./bin/mage ` to run a specific task." 39 | "Remember: You can always check available tasks with `./bin/mage`" 40 | "Fun fact: The word 'mage' comes from the Latin 'magus', meaning 'wise'" 41 | "Pro tip: You can setup autocomplete for mage to speed up your workflow with mage setup-autocomplete." 42 | "Tip: we reccomend aliasing mage like: `alias mage='cd $MB_DIR && ./bin/mage'`"] 43 | (zen)))) 44 | 45 | (defn- print-help [] 46 | (lolcat "./mage/resources/splash.txt") 47 | (flush) 48 | (println (c/bold " ✨ Metabase Automation Genius Engine ✨")) 49 | (println "") 50 | (println (u/sh "./bin/bb tasks")) 51 | (println (tip-o-day))) 52 | 53 | (defn- summarize-exception [^Exception e] 54 | (cond-> {:exception-type (-> e class .getName) 55 | :ex-message (.getMessage e)} 56 | 57 | ;; Include ex-data if it's an ExceptionInfo 58 | (instance? clojure.lang.ExceptionInfo e) 59 | (assoc :data (ex-data e)) 60 | 61 | ;; Recursively handle cause 62 | (.getCause e) 63 | (assoc :cause (summarize-exception (.getCause e))))) 64 | 65 | (defn -main [& _] 66 | (cond 67 | ;; help 68 | (or 69 | (nil? *command-line-args*) 70 | (= *command-line-args* ["-h"]) 71 | (= *command-line-args* ["--help"])) 72 | (print-help) 73 | 74 | ;; errors 75 | (invalid-task?) 76 | (do (print-help) (System/exit 1)) 77 | 78 | :else 79 | ;; at this point, we always have a valid task, and we are running in bb, so 80 | ;; we can call the task directly with `bt/run`. 81 | (try 82 | ;; the task is the first command-line argument 83 | (let [[task & args] *command-line-args*] 84 | (binding [*command-line-args* args] 85 | (try (bt/run task) 86 | (catch Exception e 87 | ;; To signal a problem in your mage task: 88 | ;; - Exceptions get summarized unless you set MAGE_DEBUG in your environment 89 | ;; so they aren't so noisy 90 | ;; - The ex-message will be printed 91 | ;; - The ex-data will be printed 92 | ;; - the (optional) `:babashka/exit` ex-data key will be used to determine the exit code 93 | ;; - the (optional) `:mage/error` ex-data key will suppress printing the entire exception, 94 | ;; use this when you know what the problem is and want to avoid noise 95 | ;; 96 | ;; Avoid using `System/exit` in your tasks, as it will hurt repl-ability of tasks: it will close the repl! 97 | (let [message (ex-message e) 98 | data (ex-data e)] 99 | (when (and e (not (:mage/error data))) 100 | (println (c/yellow "\nException:\n") 101 | (cond-> e 102 | (not (u/env "MAGE_DEBUG" (constantly nil))) 103 | summarize-exception))) 104 | (when (and message (not (str/blank? message))) 105 | (println (c/red (c/reverse-color "ex-message : ")) message)) 106 | (when data 107 | (println (c/yellow (c/reverse-color "ex-data : ")) (pr-str 108 | (dissoc data :mage/error)))) 109 | (when (:mage/error data) 110 | (println (c/blue (c/reverse-color "mage/error : ")) (:mage/error data))) 111 | (System/exit (:babashka/exit data 1)))))))))) 112 | 113 | (when (= *file* (System/getProperty "babashka.file")) 114 | (-main)) 115 | -------------------------------------------------------------------------------- /test/haskell.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Language.Moonbit.Mbti.Parser where 4 | 5 | import Data.Functor 6 | import Data.Maybe 7 | import Language.Moonbit.Lexer 8 | import Language.Moonbit.Mbti.Syntax 9 | import Text.Parsec.Text.Lazy (Parser) 10 | 11 | import Text.Parsec 12 | 13 | attrCtors :: [(String, Maybe String -> FnAttr)] 14 | attrCtors = 15 | [ ("deprecated", Deprecated) 16 | ] 17 | 18 | pAttr :: Parser FnAttr 19 | pAttr = do 20 | _ <- char '#' "‘#’" 21 | name <- choice (map ((try . string) . fst) attrCtors) "attribute name" 22 | arg <- optionMaybe $ parens stringLit -- optional argument for the attribute 23 | case lookup name attrCtors of 24 | Just ctor -> return $ ctor arg 25 | Nothing -> parserFail $ "unknown attribute: " ++ name 26 | 27 | -- >>> parse pTypeDecl "" "type Node[K, V]" 28 | -- Right (TypeDecl (TName Nothing (TCon "Node" [TName Nothing (TCon "K" []),TName Nothing (TCon "V" [])]))) 29 | -- >>> parse pTypeDecl "" "type Node" 30 | -- Right (TypeDecl (TName Nothing (TCon "Node" []))) 31 | 32 | pTypeDecl :: Parser Decl 33 | pTypeDecl = do 34 | _ <- reserved RWType 35 | name <- identifier 36 | tyargs <- option [] (brackets (commaSep identifier)) 37 | return $ TypeDecl (TName Nothing (TCon name $ (\n -> TName Nothing $ TCon n []) <$> tyargs)) 38 | 39 | pTFun :: Parser Type 40 | pTFun = do 41 | isAsync <- fromMaybe False <$> optionMaybe (reserved RWAsync $> True) 42 | args <- parens (commaSep pType) 43 | reservedOp OpArrow 44 | retTy <- pType 45 | effEx <- fromMaybe NoAraise <$> optionMaybe pEffectException 46 | let effects = [EffAsync | isAsync] ++ [EffException effEx] 47 | return (TFun args retTy effects) 48 | 49 | pEffectException :: Parser EffectException 50 | pEffectException = 51 | try (reserved RWRaisePoly $> AraisePoly) 52 | <|> try (reserved RWNoRaise $> NoAraise) 53 | <|> do 54 | reserved RWRaise 55 | Araise <$> pType 56 | "exception effect (raise?, noaraise or raise T)" 57 | 58 | pTTuple :: Parser Type 59 | pTTuple = TTuple <$> parens (commaSep pType) 60 | 61 | pTPath :: Parser TPath 62 | pTPath = do 63 | _ <- symbol "@" 64 | segs <- identifier `sepBy1` slash 65 | return $ TPath (init segs) (last segs) -- SAFETY: sepBy1 ensures at least one segment 66 | 67 | pTCon :: Parser TCon 68 | pTCon = do 69 | name <- identifier 70 | tyargs <- option [] (brackets (commaSep pType)) 71 | return $ TCon name tyargs 72 | 73 | -- TCon without the ? 74 | pTAtom :: Parser Type 75 | pTAtom = do 76 | mpath <- optionMaybe (pTPath <* reservedOp OpDot) 77 | TName mpath <$> pTCon 78 | 79 | pTNonFun :: Parser Type 80 | pTNonFun = do 81 | base <- try pTTuple <|> pTAtom 82 | isOpt <- option False (symbol "?" $> True) 83 | if isOpt 84 | then return $ TName Nothing (TCon "Option" [base]) 85 | else return base 86 | 87 | -- >>> parse pTFun "" "async (K, V) -> V raise E" 88 | -- Right (TFun [TName Nothing (TCon "K" []),TName Nothing (TCon "V" [])] (TName Nothing (TCon "V" [])) [EffAsync,EffException (Araise (TName Nothing (TCon "E" [])))]) 89 | 90 | -- >>> parse pTFun "" "(Int, Bool) -> String?" 91 | -- Right (TFun [TName Nothing (TCon "Int" []),TName Nothing (TCon "Bool" [])] (TName Nothing (TCon "Option" [TName Nothing (TCon "String" [])])) [EffException NoAraise]) 92 | 93 | -- >>> parse pTFun "" "async (T) -> U noaraise" 94 | -- Right (TFun [TName Nothing (TCon "T" [])] (TName Nothing (TCon "U" [])) [EffAsync,EffException NoAraise]) 95 | 96 | -- >>> parse pType "" "(A?,B) -> C raise?" 97 | -- Right (TFun [TName Nothing (TCon "Option" [TName Nothing (TCon "A" [])]),TName Nothing (TCon "B" [])] (TName Nothing (TCon "C" [])) [EffException AraisePoly]) 98 | 99 | pType :: Parser Type 100 | pType = 101 | choice 102 | [ try pTFun 103 | , pTNonFun 104 | ] 105 | 106 | -- pFnSig :: Parser FnSig 107 | -- pFnSig = do 108 | 109 | -- pType :: Parser Type 110 | -- pType = do 111 | 112 | -- >>> parse pPackageDecl "" "package \"user/repo/path/to/module\"" 113 | -- Right (ModulePath {mpUserName = "user", mpModuleName = "repo", mpPackagePath = ["path","to","module"]}) 114 | 115 | pPackageDecl :: Parser ModulePath 116 | pPackageDecl = reserved RWPackage *> pQuotedModulePath 117 | 118 | -- >>> parse pImportDecl "" "import (\"user1/repo1/path/to/module\" \"user2/repo2/another/path\")" 119 | -- Right [ModulePath {mpUserName = "user1", mpModuleName = "repo1", mpPackagePath = ["path","to","module"]},ModulePath {mpUserName = "user2", mpModuleName = "repo2", mpPackagePath = ["another","path"]}] 120 | 121 | pImportDecl :: Parser [ModulePath] 122 | pImportDecl = reserved RWImport *> parens (spaces *> pQuotedModulePath `sepBy` spaces <* spaces) 123 | 124 | -- >>> parse pModulePath "" "user/modname/path/to/module" 125 | -- Right (ModulePath {mpUserName = "user", mpModuleName = "modname", mpPackagePath = ["path","to","module"]}) 126 | 127 | pModulePath :: Parser ModulePath 128 | pModulePath = do 129 | segments <- identifier `sepBy1` slash 130 | case segments of 131 | user : modName : pName1 : rest -> return $ ModulePath user modName (pName1 : rest) -- make sure to have at least 1 segment after the module name 132 | _ -> parserFail "module path must be in the form ‘user/modname/path" 133 | 134 | pQuotedModulePath :: Parser ModulePath 135 | pQuotedModulePath = between (char '"') (char '"') pModulePath 136 | -------------------------------------------------------------------------------- /test/javascript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 4 | // usage 5 | // amp -p '*' ls -1 6 | // or... 7 | // amp git add 8 | // 9 | // bug: what to do when ~/.amp.json is not yet created. 10 | // 11 | const spawn = require('child_process').spawn 12 | const which = require('which').sync 13 | const fs = require('fs') 14 | 15 | const DB = process.env.HOME + '/.amp.json' 16 | let db 17 | let l = function () {} 18 | if (process.env.DEBUG) { 19 | l = function () { 20 | console.log.apply(console.log, arguments) 21 | } 22 | } 23 | const isRange = /^(\d+)\-(\d+)$/ 24 | const isDigit = /^(\d+)$/ 25 | 26 | let args = process.argv 27 | // toss out `node scriptname.js` 28 | args = args.slice(2) 29 | 30 | let pattern 31 | if (args[0] === '-p' || args[0] === '--pattern') { 32 | pattern = args[1] 33 | // toss out `-p mypattern` 34 | args = args.slice(2) 35 | } 36 | 37 | if (!args.length) return printVars() 38 | 39 | args = substitute(args) 40 | // console.log('post substitute >', args, '<') 41 | 42 | // l('pattern', pattern) 43 | if (pattern) return runWithPattern(pattern, args) 44 | 45 | // show what we are executing 46 | // (when no executable command is given, this just prints what was expanded) 47 | console.log(args.join(' ')) 48 | 49 | // if args[0] does not exist, or is not executable, do nothing. 50 | let cmd 51 | try { 52 | cmd = which(args[0]) 53 | } catch (e) { 54 | l(e.stack) 55 | return 56 | } 57 | 58 | // run the executable with the expanded args 59 | l(cmd, args.slice(1)) 60 | return spawn(cmd, args.slice(1) || [], { stdio: 'inherit' }).on( 61 | 'close', 62 | (code, signal) => { 63 | process.exit(code) 64 | } 65 | ) 66 | 67 | function substitute(args) { 68 | // if there is saved data from the last run, try to substitute digits/ranges 69 | if (!loadDb()) return args 70 | l('substitute.map') 71 | let newargs = [] 72 | args.forEach((arg) => { 73 | l('substitute.map', arg) 74 | l('substitute.isRange', isRange.test(arg)) 75 | l('substitute.isDigit', isDigit.test(arg)) 76 | if (isRange.test(arg)) newargs = newargs.concat(insertRange(arg)) 77 | else if (isDigit.test(arg)) newargs.push(insertDigit(arg)) 78 | else newargs.push(arg) 79 | }) 80 | return newargs 81 | } 82 | 83 | function insertRange(arg) { 84 | const args = [] 85 | 86 | l('insertRange.arg', arg) 87 | arg.replace(isRange, (match, start, end, string) => { 88 | start = parseInt(start, 10) 89 | end = parseInt(end, 10) 90 | for (let i = start; i <= end; i++) { 91 | const val = db[i] 92 | l('insertRange.val', val) 93 | if (val) args.push(val) 94 | } 95 | }) 96 | l('insertRange args', args) 97 | if (args.length) return args 98 | return arg 99 | } 100 | 101 | function insertDigit(arg) { 102 | l('digit') 103 | return arg.replace(isDigit, (match, digit, string) => { 104 | l('digit', match, typeof digit) 105 | const val = db[digit] 106 | l('val', val) 107 | 108 | if (val) return val 109 | return string 110 | }) 111 | } 112 | 113 | function runWithPattern(pattern, args) { 114 | l('runWithPattern') 115 | if ('git' === args[0]) { 116 | args = [args[0], '-c', 'color.' + args[1] + '=always'].concat(args.slice(1)) 117 | } 118 | let i = 1 119 | const re = new RegExp(pattern) 120 | 121 | console.log(args.join(' ')) 122 | const child = spawn(args[0], args.slice(1)) 123 | child.stdout.on('data', (data) => { 124 | const out = data 125 | .toString() // its a buffer 126 | .split('\n') // there are many lines 127 | out.pop() // get rid of the trailing newline 128 | 129 | const pad = out.length >= 10 ? ' ' : '' 130 | 131 | out.forEach((line) => { 132 | l('line', line) 133 | // match after stripping ANSI colors. via http://superuser.com/a/380778 134 | const m = re.exec(line.replace(/\x1b\[[0-9]*m/g, '')) 135 | if (m) { 136 | // if no capture group, just use whole string 137 | l('match', m) 138 | db[i] = m[1] || m[0] 139 | } 140 | const num = i <= 9 ? i + pad : i 141 | console.log(num + ' ' + line) 142 | i++ 143 | }) 144 | l(db) 145 | }) 146 | child.stderr.on('data', (data) => { 147 | process.stderr.write(data) 148 | }) 149 | let saved = false 150 | child.on('close', saveDB) 151 | // Also call saveDB() when the parent closes, in case of `| head`. 152 | // Prevent errors caused by trying to process.stdout.write or console.log 153 | // after stdout has been closed (e.g. when piping to head, `| head`) 154 | // https://github.com/mhart/epipebomb/issues/10 155 | require('epipebomb')(process.stdout, saveDB) 156 | function saveDB(code) { 157 | if (saved) return 158 | saved = true 159 | fs.writeFile(DB, JSON.stringify(db, null, 2), 'utf8', (err) => { 160 | if (err) console.log('Problem saving variables ' + err.stack) 161 | process.exit(code) 162 | }) 163 | } 164 | } 165 | 166 | function loadDb() { 167 | try { 168 | db = require(DB) 169 | } catch (e) { 170 | l(e) 171 | return false 172 | } 173 | return true 174 | } 175 | 176 | function printVars() { 177 | if (!loadDb()) { 178 | return console.log('No variables currently saved') 179 | } 180 | Object.keys(db).forEach((k, _, keys) => { 181 | const lhs = keys.length >= 10 ? pad(k, 2) : k 182 | console.log(lhs, '=', db[k]) 183 | }) 184 | } 185 | 186 | function pad(s, num) { 187 | while (s.length < num) s += ' ' 188 | return s 189 | } 190 | -------------------------------------------------------------------------------- /test/javascriptreact.jsx: -------------------------------------------------------------------------------- 1 | // BasicFigure.js 2 | import React from 'react'; 3 | 4 | // Component that displays a single figure with image and caption 5 | const BasicFigure = ({ imageUrl, caption }) => { 6 | return ( 7 |
8 | {/* Image element with source and alt text */} 9 | {caption} 10 | {/* Caption for the image */} 11 |

{caption}

12 |
13 | ); 14 | }; 15 | 16 | export default BasicFigure; 17 | 18 | // FigureList.js 19 | import React, { useState } from 'react'; 20 | import BasicFigure from './BasicFigure'; 21 | 22 | // Component that manages and displays a list of figures 23 | const FigureList = () => { 24 | // State to store the array of figures 25 | const [figures, setFigures] = useState([ 26 | { imageUrl: 'https://picsum.photos/400', caption: 'Random Image 1' }, 27 | { imageUrl: 'https://picsum.photos/400', caption: 'Random Image 2' }, 28 | { imageUrl: 'https://picsum.photos/400', caption: 'Random Image 3' }, 29 | { imageUrl: 'https://picsum.photos/400', caption: 'Random Image 4' }, 30 | ]); 31 | 32 | // Function to add a new figure to the list 33 | const addFigure = () => { 34 | const newFigure = { 35 | imageUrl: `https://picsum.photos/400?random=${figures.length + 1}`, 36 | caption: `Random Image ${figures.length + 1}`, 37 | }; 38 | setFigures([...figures, newFigure]); 39 | }; 40 | 41 | // Function to remove the last figure from the list 42 | const removeFigure = () => { 43 | const updatedFigures = figures.slice(0, -1); 44 | setFigures(updatedFigures); 45 | }; 46 | 47 | return ( 48 |
49 | {/* Container for action buttons */} 50 |
51 | 52 | 53 |
54 | {/* Container for the list of figures */} 55 |
56 | {/* Map through figures array and render BasicFigure for each */} 57 | {figures.map((figure, index) => ( 58 | 59 | ))} 60 |
61 |
62 | ); 63 | }; 64 | 65 | export default FigureList; 66 | 67 | // App.js 68 | import React from 'react'; 69 | import FigureList from './components/FigureList'; 70 | import './App.css'; 71 | 72 | // Main application component 73 | const App = () => { 74 | return ( 75 |
76 | {/* Application title */} 77 |

Dynamic Image Gallery

78 | {/* Render the FigureList component */} 79 | 80 |
81 | ); 82 | }; 83 | 84 | export default App; 85 | 86 | /* CSS styles for the application */ 87 | *{ 88 | /* Reset default padding and margin */ 89 | padding: 0; 90 | margin: 0; 91 | /* Use border-box sizing model */ 92 | box-sizing: border-box; 93 | } 94 | 95 | /* Style for h1 heading */ 96 | h1 { 97 | background: #000; 98 | color: #fff; 99 | padding: 10px; 100 | text-align: center; 101 | } 102 | 103 | /* Container for the figure list */ 104 | .figure-list-container { 105 | display: flex; 106 | flex-direction: column; 107 | align-items: center; 108 | margin: 20px; 109 | } 110 | 111 | /* Container for action buttons */ 112 | .button-box { 113 | display: block; 114 | text-align: center; 115 | padding: 10px; 116 | margin-bottom: 20px; 117 | } 118 | 119 | /* Style for action buttons */ 120 | .action-button { 121 | padding: 10px 20px; 122 | margin: 10px; 123 | background-color: #4CAF50; 124 | color: white; 125 | border: none; 126 | border-radius: 5px; 127 | cursor: pointer; 128 | font-size: 16px; 129 | transition: background-color 0.3s ease; 130 | } 131 | 132 | /* Hover effect for action buttons */ 133 | .action-button:hover { 134 | background-color: #45a049; 135 | } 136 | 137 | /* Container for the list of figures */ 138 | .figure-list { 139 | display: flex; 140 | flex-wrap: wrap; 141 | justify-content: center; 142 | gap: 15px; 143 | } 144 | 145 | /* Style for images in figure list */ 146 | .figure-list img { 147 | max-width: 200px; 148 | max-height: 200px; 149 | border: 2px solid #ccc; 150 | border-radius: 8px; 151 | } 152 | 153 | /* Style for figure element */ 154 | figure { 155 | display: flex; 156 | flex-direction: column; 157 | align-items: center; 158 | } 159 | 160 | /* Style for figure caption */ 161 | figcaption { 162 | margin-top: 8px; 163 | font-size: 14px; 164 | color: #555; 165 | } 166 | 167 | /* Style for individual figure container */ 168 | .figure { 169 | display: flex; 170 | flex-direction: column; 171 | align-items: center; 172 | border: 2px solid #ddd; 173 | border-radius: 8px; 174 | padding: 10px; 175 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 176 | transition: transform 0.2s ease, box-shadow 0.2s ease; 177 | } 178 | 179 | /* Hover effect for figure */ 180 | .figure:hover { 181 | transform: translateY(-5px); 182 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); 183 | } 184 | 185 | /* Style for figure image */ 186 | .figure-image { 187 | max-width: 200px; 188 | max-height: 200px; 189 | border-radius: 8px; 190 | object-fit: cover; 191 | } 192 | 193 | /* Style for figure caption text */ 194 | .figure-caption { 195 | margin-top: 10px; 196 | font-size: 14px; 197 | color: #555; 198 | text-align: center; 199 | } 200 | -------------------------------------------------------------------------------- /test/scss.scss: -------------------------------------------------------------------------------- 1 | /*-- scss:defaults --*/ 2 | 3 | // Import fonts 4 | 5 | // @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,500,600,700,800,400italic,700italic); 6 | @import url('https://fonts.googleapis.com/css?family=Alegreya:400,400i,600,700,800,700i|Alegreya+Sans'); 7 | @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap'); 8 | 9 | $font-family-sans-serif: "Alegreya", sans-serif !default; 10 | $font-family-monospace: "Fira Code", monospace !default; 11 | $font-family-code: "Fira Code", monospace !default; 12 | $presentation-font-size-root: 45px !default; 13 | $presentation-title-font-size-root: 4px !default; 14 | 15 | // $h1-font-size: 16 | 17 | // colors 18 | $main-color: #901A1E !default; 19 | $body-bg: #000000 !default; // Used to be eigengrau #16161d 20 | $body-color: #fff !default; 21 | $link-color: #901A1E !default; 22 | $selection-bg: #e7ad52 !default; 23 | $input-panel-bg: rgba(233, 236, 239, 0.2) !default; 24 | $aside-text-color: #222 !default; 25 | $gray-color: #BBBBBB !default; 26 | 27 | $table-header-bg-color: $main-color; /* Background color for table headers */ 28 | $table-zebra-bg-light: #999; 29 | $table-zebra-bg-dark: #666; 30 | 31 | // Code 32 | $code-color: $body-color; 33 | 34 | // Headings 35 | $presentation-heading-font: "Open Sans", Impact, sans-serif; 36 | $presentation-heading-letter-spacing: -0.05em; 37 | $presentation-heading-font-weight: 600; 38 | 39 | // code blocks 40 | //$code-block-border-color: rgba(233, 236, 239, 0.5) !default; 41 | $code-block-border-color: rgba(33, 236, 239, 0.5) !default; 42 | 43 | 44 | 45 | /*-- scss:rules --*/ 46 | 47 | 48 | // Adding a few default colours that can be used to emphasize text 49 | 50 | 51 | .blue { 52 | color: #0000ff; 53 | font-weight: bold; 54 | } 55 | 56 | .yellow { 57 | color: #ffff00; 58 | font-weight: bold; 59 | } 60 | 61 | /* 62 | .orange { 63 | color: #ffac1c; 64 | font-weight: bold; 65 | } 66 | 67 | .green { 68 | color: #5ce65c; 69 | font-weight: bold; 70 | } 71 | 72 | .white { 73 | color: #ffffff; 74 | font-weight: bold; 75 | } 76 | 77 | .gray { 78 | color: #777777; 79 | } 80 | */ 81 | 82 | .red { 83 | color: #ff0000; 84 | font-weight: bold; 85 | } 86 | 87 | 88 | /* Font sizes */ 89 | 90 | .Large { font-size: 144% } 91 | .large { font-size: 120% } 92 | .small { font-size: 90% } 93 | .footnotesize { font-size: 80% } 94 | .scriptsize { font-size: 70% } 95 | .tiny { font-size: 60% } 96 | 97 | 98 | 99 | .title { 100 | font-size: 150% 101 | } 102 | 103 | .subtitle { 104 | font-size: 150%; 105 | } 106 | 107 | 108 | // Fix the font for the R code 109 | .reveal code { 110 | font-family: $font-family-code; 111 | font-size: 90% ; 112 | } 113 | 114 | .reveal pre code { 115 | font-size: 120% ; 116 | } 117 | 118 | 119 | .reveal h1 { 120 | // letter-spacing: -0.0325em; 121 | } 122 | 123 | .reveal h2 { 124 | // letter-spacing: -0.0325em; 125 | } 126 | 127 | 128 | .reveal .slide aasdside { 129 | position: absolute; 130 | writing-mode: vertical-rl; 131 | right: -100px; 132 | // left: auto; 133 | bottom: 0px; 134 | top: 0%; 135 | color: #5555aa ; 136 | font-size: 50% ; 137 | text-align: left; 138 | transform: translate( 0%, 0% ) scale( 100% ) rotate(180deg); 139 | } 140 | 141 | 142 | 143 | .reveal .caption-right-vertical { 144 | 145 | // position: fixed; 146 | right: 0px; bottom: 0px; 147 | 148 | 149 | position: absolute; 150 | // writing-mode: vertical-rl; 151 | right: 8px; 152 | // bottom: 80px; 153 | color: #fff; 154 | // height: 200%; 155 | font-size: 70%; 156 | transform-origin: bottom right; 157 | transform: rotate(-90deg) translate(0%, 0%); 158 | } 159 | 160 | 161 | 162 | 163 | .reveal .slide aside { 164 | position: absolute; 165 | top: 0; 166 | left: 0; 167 | transform-origin: 100% 0; 168 | transform: rotate(90deg) translate(0%, -100%); 169 | border-left: 34px solid #369; 170 | padding-left: 10px; 171 | text-transform: uppercase; 172 | background: #369; 173 | color: #fff; 174 | /* padding: 5px 10px; 175 | margin: 0 0 10px 0; 176 | line-height: 24px; */ 177 | } 178 | 179 | 180 | 181 | /* Create a background that resembles a key */ 182 | .button { 183 | background-color: #bbbbbb; 184 | border: none; 185 | box-shadow:1px 0 1px 0 #eee, 0 2px 0 2px #ccc, 0 2px 0 3px #444; 186 | border-radius: 5px; 187 | color: black; 188 | padding: 10px 20px; 189 | text-align: center; 190 | text-decoration: none; 191 | display: inline-block; 192 | margin: 4px 2px; 193 | min-width: 40px; 194 | cursor: pointer; 195 | } 196 | 197 | 198 | 199 | 200 | .inverse { 201 | background-color: var(--inverse-bg-color); 202 | color: var(--inverse-text-color); 203 | text-shadow: 0 0 20px #333; 204 | } 205 | 206 | .inverse h1, .inverse h2, .inverse h3 { 207 | color: var(--inverse-text-color); 208 | text-shadow: 0 0 20px #333; 209 | // line-height: 0.8em; 210 | } 211 | 212 | 213 | /**************** 214 | * 215 | * Tables 216 | * 217 | ****************/ 218 | 219 | table { display: inline-block; } /* Fix centering of tables */ 220 | 221 | table td { padding: 0px 16px; } 222 | 223 | table th { // Table header 224 | background-color: $table-header-bg-color; 225 | font-weight: bold; 226 | padding: 0px 6px; 227 | } 228 | 229 | tbody tr:nth-child(odd) { /* Zebra stipes for tables */ 230 | background-color: $table-zebra-bg-dark; 231 | } 232 | 233 | tbody tr:nth-child(even) { /* Zebra stipes for tables */ 234 | background-color: $table-zebra-bg-light; 235 | color: #000; 236 | } 237 | 238 | 239 | 240 | details > summary { 241 | font-size: 50%; 242 | color: $gray-color ! important; 243 | } 244 | 245 | 246 | // Images 247 | 248 | 249 | .rotate-left { 250 | -webkit-transform: rotate(-2deg); 251 | -moz-transform: rotate(-2deg); 252 | transform: rotate(-2deg); 253 | } 254 | 255 | .rotate-right { 256 | -webkit-transform: rotate(2deg); 257 | -moz-transform: rotate(2deg); 258 | transform: rotate(2deg); 259 | } 260 | 261 | .polaroid { 262 | border: 10px solid #fff; 263 | border-bottom: 85px solid #fff; 264 | -webkit-box-shadow: 3px 3px 3px #111; 265 | -moz-box-shadow: 3px 3px 3px #111; 266 | box-shadow: 3px 3px 3px #111; 267 | } 268 | 269 | .shadow { 270 | -moz-border-radius: 10px; 271 | -moz-box-shadow: 25px 25px 25px #777; 272 | -webkit-box-shadow: 25px 25px 25px #777; 273 | box-shadow: 25px 25px 25px #777; 274 | border-radius: 10px; 275 | } 276 | -------------------------------------------------------------------------------- /test/rust.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "date"] 2 | #![crate_type = "lib"] 3 | //! Date management 4 | //! 5 | //! Use "constructor", string manipulation 6 | //! Tested with rust-1.3.0 7 | //! 8 | //! @author Eliovir 9 | //! 10 | //! @license MIT license 11 | //! 12 | //! @since 2013-10-24 13 | //! 14 | //! @todo : get_day_of_week(), get_week(), comparisons 15 | use std::fmt; 16 | 17 | /** 18 | * Simple struct to handle date. 19 | */ 20 | pub struct Date { 21 | day: u32, 22 | month: u32, 23 | year: u32 24 | } 25 | 26 | impl Date { 27 | /** 28 | * Add days to the current day. Use negative to remove day. 29 | */ 30 | pub fn add_days(&mut self, days: i32) { 31 | let mut day = self.day as i32; 32 | let mut month = self.month; 33 | let mut year = self.year; 34 | day = day + days; 35 | if days > 0 { 36 | let mut month_length = Date::month_length(year, month) as i32; 37 | while day > month_length { 38 | day = day - month_length; 39 | month = month + 1; 40 | if month > 12 { 41 | year = year + 1; 42 | month = 1; 43 | } 44 | month_length = Date::month_length(year, month) as i32; 45 | } 46 | } 47 | if day == 0 { 48 | month = month - 1; 49 | if month < 1 { 50 | month = 12; 51 | year = year - 1; 52 | } 53 | day = Date::month_length(year, month) as i32; 54 | } 55 | if days < 0 { 56 | while day < 1 { 57 | month = month - 1; 58 | if month < 1 { 59 | year = year - 1; 60 | month = 12; 61 | } 62 | let month_length = Date::month_length(year, month) as i32; 63 | day = day + month_length; 64 | } 65 | } 66 | self.day = day as u32; 67 | self.month = month; 68 | self.year = year; 69 | } 70 | 71 | /** 72 | * Get day of year. 73 | */ 74 | pub fn get_day_of_year(&self) -> u32 { 75 | let mut doy = self.day; 76 | for month in 1.. self.month { 77 | doy += Date::month_length(self.year, month); 78 | } 79 | doy 80 | } 81 | 82 | /** 83 | * Check if defined date is valid. 84 | */ 85 | pub fn is_valid(&self) -> bool { 86 | if self.month < 1 || self.month > 12 { 87 | false 88 | } else if self.day < 1 || self.day > Date::month_length(self.year, self.month) { 89 | false 90 | } else { 91 | true 92 | } 93 | } 94 | 95 | /* 96 | * Static methods 97 | */ 98 | 99 | /** 100 | * Static method to know if the year is a leap year. 101 | */ 102 | pub fn is_leap(year: u32) -> bool { 103 | (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 104 | } 105 | 106 | /** 107 | * Static method to get the number of days in the month. 108 | */ 109 | pub fn month_length(year: u32, month: u32) -> u32 { 110 | match month { 111 | 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 112 | 2 => if Date::is_leap(year) { 29 } else { 28 }, 113 | 4 | 6 | 9 | 11 => 30, 114 | _ => panic!("Wrong month") 115 | } 116 | } 117 | 118 | /** 119 | * "Constructor". 120 | */ 121 | pub fn new(year: u32, month: u32, day: u32) -> Date { 122 | Date{day: day, month: month, year: year} 123 | } 124 | 125 | /** 126 | * "Constructor" using string like "2013-10-24". 127 | */ 128 | pub fn new_from_string(string: &str) -> Date { 129 | if string.len() < 10 { 130 | panic!("Wrong format!"); 131 | } 132 | let year : u32 = string[0..4].parse().ok().expect("Wrong format!"); 133 | let month : u32 = string[5..7].parse().ok().expect("Wrong format!"); 134 | let day : u32 = string[8..10].parse().ok().expect("Wrong format!"); 135 | Date{day: day, month: month, year: year} 136 | } 137 | } 138 | 139 | /** 140 | * Operator overloading 141 | * 142 | * @see 143 | */ 144 | impl fmt::Display for Date { 145 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 146 | write!(f, "{:4}-{:2}-{:2}", self.year, self.month, self.day) 147 | } 148 | } 149 | 150 | #[cfg(test)] 151 | mod tests { 152 | #[test] 153 | fn add_days() { 154 | let mut date = ::Date::new(2013, 10, 24); 155 | let orig = date.to_string(); 156 | let days = 2; 157 | date.add_days(days); 158 | let expected = "2013-10-26".to_string(); 159 | let found = date.to_string(); 160 | assert!(expected==found, "Adding {} days to {} should return {}, not {}", days, orig, expected, found); 161 | } 162 | #[test] 163 | fn get_day_of_year() { 164 | let date = ::Date::new(2014, 01, 01); 165 | let expected = 1; 166 | let found = date.get_day_of_year(); 167 | assert!(expected==found, "{} must be day number {} of the year, not {}.", date, expected, found); 168 | let date = ::Date::new(2012, 12, 31); 169 | let expected = 366; 170 | let found = date.get_day_of_year(); 171 | assert!(expected==found, "{} must be day number {} of the year, not {}.", date, expected, found); 172 | } 173 | #[test] 174 | fn is_leap() { 175 | assert!(!::Date::is_leap(1900), "1900 is not a leap year"); 176 | assert!(!::Date::is_leap(1901), "1901 is not a leap year"); 177 | assert!(::Date::is_leap(2000), "2000 is leap year"); 178 | assert!(::Date::is_leap(2004), "2004 is leap year"); 179 | } 180 | #[test] 181 | fn is_valid() { 182 | let mut date = ::Date::new(2013, 10, 24); 183 | assert!(date.is_valid(), "2013-10-24 is a valid date"); 184 | date = ::Date::new(2013, 02, 29); 185 | assert!(!date.is_valid(), "2013-02-29 isn't a valid date"); 186 | date = ::Date::new(2012, 02, 29); 187 | assert!(date.is_valid(), "2012-02-29 isn't a valid date"); 188 | } 189 | #[test] 190 | fn to_string() { 191 | let date = ::Date::new(2013, 10, 24); 192 | let expected = "2013-10-24"; 193 | let found = date.to_string(); 194 | assert!(expected == found, "{}!={}", expected, found); 195 | } 196 | /* 197 | * Static methods 198 | */ 199 | #[test] 200 | fn month_length() { 201 | assert!(::Date::month_length(2000, 2) == 29, "February 2000 has 29 days"); 202 | assert!(::Date::month_length(2001, 2) == 28, "February 2001 has 28 days"); 203 | assert!(::Date::month_length(2013, 2) == 28, "February 2013 has 28 days"); 204 | assert!(::Date::month_length(2013, 9) == 30, "September 2013 has 30 days"); 205 | assert!(::Date::month_length(2013, 10) == 31, "October 2013 has 31 days"); 206 | } 207 | #[test] 208 | fn new() { 209 | let date = ::Date::new(2013, 10, 24); 210 | let expected = "2013-10-24".to_string(); 211 | let found = date.to_string(); 212 | assert!(expected == found, "{}!={}", expected, found); 213 | } 214 | #[test] 215 | fn new_from_string() { 216 | let date = ::Date::new_from_string("2013-10-24 23:24:34"); 217 | let expected = "2013-10-24".to_string(); 218 | let found = date.to_string(); 219 | assert!(expected == found, "{}!={}", expected, found); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /test/c.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- */ 2 | /* J-Source Version 7 - COPYRIGHT 1993 Iverson Software Inc. */ 3 | /* 33 Major Street, Toronto, Ontario, Canada, M5S 2K9, (416) 925 6096 */ 4 | /* */ 5 | /* J-Source is provided "as is" without warranty of any kind. */ 6 | /* */ 7 | /* J-Source Version 7 license agreement: You may use, copy, and */ 8 | /* modify the source. You have a non-exclusive, royalty-free right */ 9 | /* to redistribute source and executable files. */ 10 | /* ----------------------------------------------------------------------- */ 11 | /* */ 12 | /* Representations - Functions for converting between different */ 13 | /* representations of J objects */ 14 | 15 | #include "j.h" 16 | 17 | /* drr - Deep Representation Reduction */ 18 | static F1(drr){PROLOG;A df,dg,fs,gs,*x,z;B b;C c,id;I fl,m;V*v; 19 | RZ(w); 20 | // If input is a noun or name, return as-is 21 | if(AT(w)&NOUN+NAME)R w; 22 | v=VAV(w); id=v->id; fl=v->fl; fs=v->f; gs=v->g; m=!!fs+!!gs; 23 | // If no function/operand, just return the spelling 24 | if(!m)R spellout(id); 25 | // If evokes to a noun, return that noun or its spelling 26 | if(evoke(w))R CA==ctype[c=cf(fs)]?fs:spellout(c); 27 | b=id==CHOOK||id==CHOOKO||id==CADVF; c=id==CFORK||id==CFORKO; 28 | // Recursively process function/operand parts 29 | if(fs)df=fl&VGERL?every(every(fs,fx),drr):drr(fs); 30 | if(gs)dg=fl&VGERR?every(every(gs,fx),drr):drr(gs); 31 | // Create result array 32 | GA(z,BOX,m+!b,1,0); x=(A*)AV(z); 33 | RZ(x[0]=df); 34 | RZ(x[1]=b||c?dg:spellout(id)); 35 | if(!b&&1h):dg); 36 | EPILOG(z); 37 | } 38 | 39 | /* drep - Deep Representation */ 40 | F1(drep){A z; R(z=drr(w),z&&AT(z)&BOX+BOXK)?z:ravel(box(z));} 41 | 42 | /* aro - Atomic Representation */ 43 | F1(aro){A fs,gs,*u,*x,y,z;C c,id;I m;V*v; 44 | RZ(w); 45 | if(FUNC&AT(w)){ 46 | v=VAV(w); id=v->id; fs=v->f; gs=v->g; 47 | m=id==CFORK||id==CFORKO?3:!!fs+!!gs; 48 | if(!m)R spellout(id); 49 | } 50 | // Create result array with 2 boxes 51 | GA(z,BOX,2,1,0); x=(A*)AV(z); 52 | if(NOUN&AT(w)){*x++=str(1L,"0"); *x=w; R z;} 53 | // Create array for function parts 54 | GA(y,BOX,m,1,0); u=(A*)AV(y); 55 | if(0h)); 58 | RZ(*x++=spellout(id)); 59 | *x=y; 60 | R z; 61 | } 62 | 63 | /* arep - Atomic Representation wrapper */ 64 | F1(arep){R box(aro(w));} 65 | 66 | /* fxr - Fixed execution with error checking */ 67 | static F1(fxr){PROLOG;A z; RZ(z=fx(w)); ASSERT(AT(z)&NOUN+VERB,EVDOMAIN); EPILOG(z);} 68 | 69 | /* fx - Fixed execution (parse and execute) */ 70 | F1(fx){A arg,fs,*u,*x,y;C b,id;I n; 71 | RZ(w); 72 | b=BOX&AT(w); u=(A*)AV(w); y=b?u[0]:w; arg=u[1]; 73 | // Validate input 74 | ASSERT(1>=AR(w),EVRANK); 75 | ASSERT(b||CHAR&AT(w),EVDOMAIN); 76 | ASSERT(!b||2==AN(w),EVLENGTH); 77 | RZ(vs(y)); 78 | ASSERT(id=spellin(AN(y),AV(y)),EVSPELL); 79 | if(C9==ctype[id]){ 80 | // Handle special cases (hooks, forks, etc.) 81 | ASSERT(b,EVDOMAIN); 82 | if('0'==id)R arg; 83 | ASSERT(1>=AR(arg),EVRANK); 84 | ASSERT(BOX&AT(arg),EVDOMAIN); 85 | n=AN(arg); x=(A*)AV(arg); 86 | switch(id){ 87 | case '2': ASSERT(2==n,EVLENGTH); R hook(fx(x[0]),fx(x[1])); 88 | case '3': ASSERT(3==n,EVLENGTH); R folk(fx(x[0]),fx(x[1]),fx(x[2])); 89 | case '4': ASSERT(2==n,EVLENGTH); R advform(fx(x[0]),fx(x[1])); 90 | case '5': ASSERT(2==n,EVLENGTH); R hooko(fx(x[0]),fx(x[1])); 91 | case '6': ASSERT(3==n,EVLENGTH); R forko(fx(x[0]),fx(x[1]),fx(x[2])); 92 | default: ASSERT(0,EVDOMAIN); 93 | }}else{ 94 | // Normal function case 95 | RZ(fs=ds(id)); 96 | ASSERT(RHS&AT(fs),EVDOMAIN); 97 | if(!b)R fs; 98 | ASSERT(1>=AR(arg),EVRANK); 99 | n=AN(arg); x=(A*)AV(arg); 100 | if(!n)R fs; 101 | ASSERT(BOX&AT(arg),EVDOMAIN); 102 | ASSERT(nc(fs)==3+n,EVLENGTH); 103 | R 1==n ? df1(fxr(x[0]),fs) : df2(fxr(x[0]),fxr(x[1]),fs); 104 | }} 105 | 106 | /* sr1 - Helper for srep */ 107 | static F1(sr1){R srep(dash,w);} 108 | 109 | /* srep - String representation */ 110 | F2(srep){PROLOG;A*v,y,z;C s[13];I m,t; 111 | RZ(a&&w); 112 | GA(y,BOX,7,1,0); v=(A*)AV(y); 113 | t=AT(w); if(t&FUNC)RZ(w=aro(w)); 114 | // Determine type code 115 | RZ(v[1]=cstr(t&CHAR+NAME?"c":t&NUMERIC?"n":t&BOX?"xb":t&BOXK?"xk": 116 | t&VERB?"xv":t&ADV?"xa":t&CONJ?"xc":"?")); 117 | t=AT(w); 118 | RZ(v[2]=NAME&AT(a)?str(AN(a),AV(a)):a); 119 | sprintf(s," %ld ",AR(w)); RZ(v[3]=cstr(s)); 120 | if(AR(w)){RZ(v[4]=thorn1(shape(w))); RZ(v[5]=scc(' '));} else v[4]=v[5]=mtv; 121 | // Handle boxed and unboxed values differently 122 | RZ(v[6]=t&BOX+BOXK?raze(every(t&BOX?w:kbox(w),sr1)):thorn1(ravel(w))); 123 | // Calculate total length 124 | m=0; DO(AN(y)-1,m+=AN(v[1+i]);); sprintf(s,"%ld",m); RZ(v[0]=cstr(s)); 125 | z=raze(y); 126 | EPILOG(z); 127 | } 128 | 129 | /* unw - Unwrap string representation into J object */ 130 | static A unw(n,s,s1,b)B b;I n;C*s,**s1;{A nm=0,*x,sh,z;C c,*s0=s,*t; 131 | I d,k,m,n0=n,p,q,r,*v; 132 | RZ(s=fi(s,&m)); *s1=m+s; q=s-s0; 133 | ASSERT(6<=m&&m<=n-q,EVLENGTH); 134 | c=*s++; d='c'==c||'p'==c?CHAR:'n'==c?INT:0; 135 | if('x'==c){c=*s++; d='a'==c?ADV:'b'==c?BOX:'k'==c?BOXK:'c'==c?CONJ:'v'==c?VERB:0;} 136 | ASSERT(d&&(b||d&NOUN),EVDOMAIN); 137 | t=strchr(s,' '); k=t-s; ASSERT(t&&k,EVILNAME); 138 | if(1=m-k)RZ(z=str(m-k,s)) 147 | else if(d&INT)RZ(z=connum(m-k,s)) 148 | else{I pp=d&BOXK?p+p:p; 149 | GA(z,d&BOXK?d:BOX,p,r,v); x=(A*)AV(z); 150 | DO(pp, RZ(*x++=unw(n,t=s,&s,0)); n-=s-t; ASSERT(n||i==pp-1,EVLENGTH);); 151 | ASSERT(m==n0-n,EVLENGTH); 152 | } 153 | ASSERT(p==AN(z),EVLENGTH); 154 | if(d&FUNC){RZ(z=fx(z)); ASSERT(d&AT(z),EVDOMAIN);} 155 | else if(d&CHAR+INT)RZ(z=reshape(sh,z)); 156 | symbis(nm,z,global); 157 | R z; 158 | } 159 | 160 | /* unsr - Unstring representation (main entry point) */ 161 | F1(unsr){A z=mtv;C*s,*t;I n; 162 | RZ(vs(w)); 163 | s=t=(C*)AV(w); while(' '==*s)++s; 164 | if(n=AN(w)-(s-t))do{RZ(z=unw(n,t=s,&s,1)); while(' '==*s)++s;}while(n-=s-t); 165 | R z; 166 | } 167 | -------------------------------------------------------------------------------- /test/typescriptreact.tsx: -------------------------------------------------------------------------------- 1 | import { computed, observable } from "mobx" 2 | import { observer } from "mobx-react" 3 | import * as React from "react" 4 | import { render } from "react-dom" 5 | import { Bracket } from "react-tournament-bracket" 6 | import { GameComponentProps } from "react-tournament-bracket/lib/components/Bracket" 7 | import { Game, SideInfo } from "react-tournament-bracket/lib/components/model" 8 | import BracketGame from "./BracketGame" 9 | // copy of the final data from the websocket 10 | // examples 11 | type Events = 12 | | { 13 | event: "score" 14 | scores: [Competitor, Competitor] 15 | } 16 | | { event: "start"; bracket: BracketData } 17 | 18 | type Competitor = { score: number; competitor: string } 19 | type BracketData = { 20 | upcoming: [["🐍", "🐉"], ["🐳", "🐬"]] 21 | current: { 22 | extra: { 23 | commentary: [ 24 | "Uh oh. 🦜 has had words with 🐢 before. The next match will be good." 25 | ] 26 | end_time: "2019-04-01T19:44:20.70275514Z" 27 | } 28 | game: [Competitor, Competitor] 29 | } 30 | played: [ 31 | [ 32 | { 33 | extra: { 34 | commentary: string[] /*[ 35 | "Duck... duck... duck... duck... owl!", 36 | "You just know that 🦆 is thinking about 🏥 right now.", 37 | "Will it be 🦆 or 🦉? Find out next after this message from our sponsors." 38 | ]*/ 39 | end_time: string /*"2019-04-01T19:43:05.701366707Z"*/ 40 | } 41 | game: [ 42 | Competitor, //{ score: 6558; competitor: "🦆" }, 43 | Competitor 44 | ] 45 | } 46 | ] 47 | ] 48 | } 49 | 50 | const totalMaxLevel = 8 51 | function GameComponent(props: GameComponentProps) { 52 | return ( 53 | ( 55 | 56 | {props.game.sides.home.team 57 | ? props.game.sides.home.team.name 58 | : "?"} 59 | {" vs "} 60 | {props.game.sides.visitor.team 61 | ? props.game.sides.visitor.team.name 62 | : "?"} 63 | {": "}"{props.game.name}" 64 | 65 | )} 66 | {...props} 67 | /> 68 | ) 69 | } 70 | 71 | const done = true 72 | @observer 73 | class UI extends React.Component { 74 | @observable startLevel = 2 75 | @observable frequentUpdates = false 76 | // ws: WebSocket 77 | 78 | @observable bracket: BracketData | null = null 79 | constructor(props: {}) { 80 | super(props) 81 | Object.assign(window, { ui: this }) 82 | if (done) 83 | import("./final.json").then(data => (this.bracket = data.bracket)) 84 | else { 85 | const ws = new WebSocket("wss://emojidome.xkcd.com/2131/socket") 86 | ws.addEventListener("message", this.onMessage) 87 | } 88 | } 89 | onMessage = (message: MessageEvent) => { 90 | const data: Events = JSON.parse(message.data) 91 | console.log(data.event) 92 | if (data.event === "start") { 93 | this.bracket = data.bracket 94 | } else if ( 95 | data.event === "score" && 96 | this.frequentUpdates === true && 97 | this.bracket !== null 98 | ) { 99 | this.bracket.current.game = data.scores 100 | } 101 | } 102 | 103 | @computed 104 | get renderableData(): Game | null { 105 | if (this.bracket === null) return null 106 | const startLevel = this.startLevel 107 | const maxLevel = totalMaxLevel - startLevel 108 | const levels = this.bracket.played 109 | .map(level => level.slice().reverse()) 110 | .reverse() 111 | .slice(startLevel) 112 | if (levels.length > 0) 113 | levels[levels.length - 1].push( 114 | this.bracket.current, 115 | ...this.bracket.upcoming.map(([l, r]) => ({ 116 | extra: { commentary: [], end_time: "unknown" }, 117 | game: [ 118 | { score: 0, competitor: l }, 119 | { score: 0, competitor: r }, 120 | ] as [Competitor, Competitor], 121 | })), 122 | ) 123 | for (const [i, level] of levels.entries()) { 124 | if (level.length !== 2 ** (maxLevel - i)) 125 | console.log( 126 | "level", 127 | i, 128 | "should be length", 129 | 2 ** (maxLevel - i), 130 | "but is", 131 | level.length, 132 | ) 133 | } 134 | function getGame(level: number, index: number): Game | null { 135 | if (level < 0) return null 136 | const info = level < levels.length ? levels[level][index] : null 137 | 138 | function mkSide( 139 | competitor: Competitor | null, 140 | offset: 0 | 1, 141 | ): SideInfo { 142 | const sourceGame = getGame(level - 1, index * 2 + offset) 143 | return { 144 | score: competitor ? { score: competitor.score } : undefined, 145 | team: { 146 | id: competitor ? competitor.competitor : "unknown", 147 | name: competitor ? competitor.competitor : "unknown", 148 | }, 149 | seed: sourceGame 150 | ? { 151 | displayName: "idk lol", 152 | rank: 1, 153 | sourceGame, 154 | sourcePool: (null as any) as object, 155 | } 156 | : undefined, 157 | } 158 | } 159 | 160 | return { 161 | id: `level-${level},index-${index}`, 162 | name: info 163 | ? info.extra.commentary[0] 164 | : `Unknown (${level}-${index})`, 165 | //bracketLabel: "yo", 166 | scheduled: info ? new Date(info.extra.end_time).valueOf() : NaN, 167 | sides: { 168 | home: mkSide(info ? info.game[0] : null, 0), 169 | visitor: mkSide(info ? info.game[1] : null, 1), 170 | }, 171 | } 172 | } 173 | const game = getGame(maxLevel, 0) 174 | console.log(game) 175 | return game 176 | } 177 | click = (game: Game) => { 178 | this.selectedGame = game 179 | } 180 | @observable selectedGame: Game | null = null 181 | render() { 182 | return ( 183 |
184 |

185 | Emojidome Live Bracket Viewer{" "} 186 | 187 | 188 | 189 | 190 | Source on GitHub 191 | 192 | 193 | 194 | 195 |

196 | 197 |
198 | Hiding first {this.startLevel} level(s){" "} 199 | {this.startLevel > 0 && ( 200 | 201 | )} 202 | {this.startLevel < totalMaxLevel && ( 203 | 204 | )} 205 |
206 | {!done && ( 207 |
208 | 217 |
218 | )} 219 | {this.renderableData ? ( 220 | 226 | ) : ( 227 |
loading...
228 | )} 229 |
230 | ) 231 | } 232 | } 233 | 234 | render(, document.getElementById("root")) 235 | -------------------------------------------------------------------------------- /test/php.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | addReader($adapter) 41 | ->addWriter($adapter); 42 | } 43 | } else { // V4 44 | $adapters = array_map(function ($adapterClass) { 45 | return new $adapterClass(); 46 | }, $adapters); 47 | 48 | $repository = RepositoryBuilder::create() 49 | ->withReaders($adapters) 50 | ->withWriters($adapters); 51 | } 52 | 53 | Dotenv::create( 54 | $repository->immutable()->make(), 55 | __DIR__ 56 | )->safeLoad(); 57 | } else { // V3 58 | Dotenv::create(__DIR__, null, new DotenvFactory([ 59 | new V3EnvConstAdapter, new V3ServerConstAdapter 60 | ]))->safeLoad(); 61 | } 62 | })(); 63 | 64 | /** 65 | * Create the container instance. 66 | */ 67 | Container::setInstance($container = new Container); 68 | 69 | require __DIR__.'/services.php'; 70 | 71 | /** 72 | * Start the console application. 73 | */ 74 | $app = new Application('Laravel Vapor', '1.10.4'); 75 | 76 | // Authentication... 77 | $app->add(new Commands\LoginCommand); 78 | 79 | // Teams... 80 | $app->add(new Commands\TeamListCommand); 81 | $app->add(new Commands\TeamCommand); 82 | $app->add(new Commands\TeamCurrentCommand); 83 | $app->add(new Commands\TeamSwitchCommand); 84 | $app->add(new Commands\MemberListCommand); 85 | $app->add(new Commands\MemberAddCommand); 86 | $app->add(new Commands\MemberRemoveCommand); 87 | $app->add(new Commands\FireCommand); 88 | 89 | // Providers... 90 | $app->add(new Commands\ProviderListCommand); 91 | $app->add(new Commands\ProviderCommand); 92 | $app->add(new Commands\ProviderUpdateCommand); 93 | $app->add(new Commands\ProviderDeleteCommand); 94 | 95 | // Zones... 96 | $app->add(new Commands\ZoneListCommand); 97 | $app->add(new Commands\ZoneCommand); 98 | $app->add(new Commands\ZoneDeleteCommand); 99 | 100 | // Records... 101 | $app->add(new Commands\RecordListCommand); 102 | $app->add(new Commands\RecordCommand); 103 | $app->add(new Commands\RecordDeleteCommand); 104 | 105 | // Certificates... 106 | $app->add(new Commands\CertListCommand); 107 | $app->add(new Commands\CertCommand); 108 | $app->add(new Commands\CertValidateCommand); 109 | $app->add(new Commands\CertDeleteCommand); 110 | 111 | // Networks... 112 | $app->add(new Commands\NetworkListCommand); 113 | $app->add(new Commands\NetworkCommand); 114 | $app->add(new Commands\NetworkShowCommand); 115 | $app->add(new Commands\NetworkNatCommand); 116 | $app->add(new Commands\NetworkDeleteNatCommand); 117 | $app->add(new Commands\NetworkDeleteCommand); 118 | 119 | // Databases... 120 | $app->add(new Commands\DatabaseListCommand); 121 | $app->add(new Commands\DatabaseCommand); 122 | $app->add(new Commands\DatabaseShowCommand); 123 | $app->add(new Commands\DatabaseScaleCommand); 124 | $app->add(new Commands\DatabasePasswordCommand); 125 | $app->add(new Commands\DatabaseProxyCommand); 126 | $app->add(new Commands\DatabaseDeleteProxyCommand); 127 | $app->add(new Commands\DatabaseRestoreCommand); 128 | $app->add(new Commands\DatabaseMetricsCommand); 129 | $app->add(new Commands\DatabaseUsersCommand); 130 | $app->add(new Commands\DatabaseUserCommand); 131 | $app->add(new Commands\DatabaseDropUserCommand); 132 | $app->add(new Commands\DatabaseShellCommand); 133 | $app->add(new Commands\DatabaseTunnelCommand); 134 | $app->add(new Commands\DatabaseDeleteCommand); 135 | 136 | // Caches... 137 | $app->add(new Commands\CacheListCommand); 138 | $app->add(new Commands\CacheCommand); 139 | $app->add(new Commands\CacheShowCommand); 140 | $app->add(new Commands\CacheScaleCommand); 141 | $app->add(new Commands\CacheMetricsCommand); 142 | $app->add(new Commands\CacheTunnelCommand); 143 | $app->add(new Commands\CacheDeleteCommand); 144 | 145 | // Jumpboxes... 146 | $app->add(new Commands\JumpListCommand); 147 | $app->add(new Commands\JumpCommand); 148 | $app->add(new Commands\JumpDeleteCommand); 149 | 150 | // Balancers... 151 | $app->add(new Commands\BalancerListCommand); 152 | $app->add(new Commands\BalancerCommand); 153 | $app->add(new Commands\BalancerDeleteCommand); 154 | 155 | // Projects... 156 | $app->add(new Commands\InitCommand); 157 | $app->add(new Commands\UiCommand); 158 | $app->add(new Commands\ProjectDeleteCommand); 159 | $app->add(new Commands\ProjectDescribeCommand); 160 | 161 | // Environments... 162 | $app->add(new Commands\EnvListCommand); 163 | $app->add(new Commands\EnvCommand); 164 | $app->add(new Commands\EnvDescribeCommand); 165 | $app->add(new Commands\EnvPullCommand); 166 | $app->add(new Commands\EnvPushCommand); 167 | $app->add(new Commands\EnvCloneCommand); 168 | $app->add(new Commands\EnvDeleteCommand); 169 | 170 | // Secrets 171 | $app->add(new Commands\SecretListCommand); 172 | $app->add(new Commands\SecretCommand); 173 | $app->add(new Commands\SecretPassportCommand); 174 | $app->add(new Commands\SecretDeleteCommand); 175 | 176 | // Deployments... 177 | $app->add(new Commands\DeployListCommand); 178 | $app->add(new Commands\BuildCommand); 179 | $app->add(new Commands\DeployCommand); 180 | $app->add(new Commands\RedeployCommand); 181 | $app->add(new Commands\HookLogCommand); 182 | $app->add(new Commands\HookOutputCommand); 183 | 184 | // Rollbacks / Maintenance Mode... 185 | $app->add(new Commands\RollbackCommand); 186 | $app->add(new Commands\DownCommand); 187 | $app->add(new Commands\UpCommand); 188 | 189 | // Commands / Invocations... 190 | $app->add(new Commands\CommandCommand); 191 | $app->add(new Commands\CommandLogCommand); 192 | $app->add(new Commands\TinkerCommand); 193 | 194 | // Logs... 195 | $app->add(new Commands\TailCommand); 196 | 197 | // Metrics... 198 | $app->add(new Commands\MetricsCommand); 199 | 200 | // Docker... 201 | $app->add(new Commands\LocalCommand); 202 | $app->add(new Commands\TestCommand); 203 | 204 | $app->run(); 205 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.8.0](https://github.com/my-rubbish/nvim.comment-hide/compare/v1.7.0...v1.8.0) (2025-12-10) 4 | 5 | 6 | ### Features 7 | 8 | * add hash test ([305cc50](https://github.com/my-rubbish/nvim.comment-hide/commit/305cc5002b37af571e5109bcd17217d7c4e8e6e1)) 9 | * add zsh ([9065724](https://github.com/my-rubbish/nvim.comment-hide/commit/9065724ac4c52fcc6584969a5910be8f2a781ec8)) 10 | 11 | 12 | ### Code Refactoring 13 | 14 | * string and special context detection logic [#25](https://github.com/my-rubbish/nvim.comment-hide/issues/25) ([1e2a262](https://github.com/my-rubbish/nvim.comment-hide/commit/1e2a262540829a06dbee878bd7d3c1a97c03d357)) 15 | 16 | ## [1.7.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.6.0...v1.7.0) (2025-08-26) 17 | 18 | 19 | ### Features 20 | 21 | * add nim lang ([013629b](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/013629b4d8a06ed2ea7615232c39209ee262e8a5)) 22 | 23 | ## [1.6.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.5.0...v1.6.0) (2025-07-28) 24 | 25 | 26 | ### Features 27 | 28 | * add clojure support close [#18](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/18) ([714aa37](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/714aa37a79003184c0204e30d11f0e2e01cb326c)) 29 | * add haskell support close [#19](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/19) ([5cbd4bb](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/5cbd4bbbd0f7362eef0c36c8307b129b8d02df9b)) 30 | * add new content ([29864fd](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/29864fd393256a625e511370196f480e73c67ebf)) 31 | 32 | ## [1.5.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.4.0...v1.5.0) (2025-07-14) 33 | 34 | 35 | ### Features 36 | 37 | * add yaml support close [#16](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/16) ([8b9aa23](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/8b9aa23d8cd0d5af1ea5ace399636ef5b8382470)) 38 | 39 | ## [1.4.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.3.0...v1.4.0) (2025-07-12) 40 | 41 | 42 | ### Features 43 | 44 | * close [#14](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/14) and fix nix file #!/bin/sh del error ([30eee9a](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/30eee9a8c710db327619d9874ab5af787107c837)) 45 | 46 | 47 | ### Code Refactoring 48 | 49 | * add [#13](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/13) support and test e.g ([f8fa4d2](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/f8fa4d250824a3aa11e493e42deaff064a35a783)) 50 | 51 | ## [1.3.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.2.0...v1.3.0) (2025-06-17) 52 | 53 | 54 | ### Features 55 | 56 | * add support handlebars template ([1331492](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/13314927ecdfaa843a4abaa83f377bc12b672b35)) 57 | 58 | ## [1.2.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.1.1...v1.2.0) (2025-05-21) 59 | 60 | 61 | ### Features 62 | 63 | * basic support for erlang and elixir [#8](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/8) ([1897835](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/18978355f644765a76fed2710ccdb5e6863b325c)) 64 | 65 | 66 | ### CI 67 | 68 | * add support lang template ([7c317f7](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/7c317f78b2386fe0d74eef5f3a78f12856eb9b47)) 69 | 70 | ## [1.1.1](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.1.0...v1.1.1) (2025-05-16) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * bash and shell file support and fix [#5](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/5) ([01f7640](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/01f7640ecb933ae68f2d76164aecc4ae2592ea59)) 76 | 77 | ## [1.1.0](https://github.com/jiangxue-analysis/nvim.comment-hide/compare/v1.0.0...v1.1.0) (2025-05-10) 78 | 79 | 80 | ### Features 81 | 82 | * add gitignore config auto .annotations/ to .gitignore [#3](https://github.com/jiangxue-analysis/nvim.comment-hide/issues/3) ([1a15ad8](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/1a15ad8c68b1fb49bddb2b2bfa5aceda0a0b6eef)) 83 | 84 | 85 | ### Docs 86 | 87 | * add gitignore config desc ([54fa7c4](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/54fa7c4f5f1eb5a3eb1727da2a8f2d3c8e0601b7)) 88 | * typo ([1b84503](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/1b845037e9bfd31ff8f61daa2d0d85f127aeb20b)) 89 | 90 | ## 1.0.0 (2025-05-07) 91 | 92 | 93 | ### Features 94 | 95 | * add docs and workflow ([e142077](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/e14207738542bfcd1e75d696e0f4cfbce088abe0)) 96 | * add support scala lang ([dfefd91](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/dfefd9189ffa1560d4dbf6cbcbf9aac9b6907ebf)) 97 | * project init ([360c164](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/360c16460aeb3880933493d9a1dbef121a9ba67f)) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * `` style code error hide ([464647b](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/464647b5cfbea3f76c5fb94142dd43d67e2e6d8a)) 103 | * python ' and " comment no hide error ([a6c840f](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/a6c840fe6b4e5030dd6f5de99d2eb82cb316a742)) 104 | * ruby and outher lang error ([f6efab9](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/f6efab9842fa70411199a542626af6e8101fbdb7)) 105 | 106 | 107 | ### Docs 108 | 109 | * add scala ([8f4b738](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/8f4b73867f4d2aa47257e3d5e17730a0798d4a0c)) 110 | * add test lang comment info ([2f53894](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/2f538940dbc74adbb624a7cf8d82394c0fd04914)) 111 | * add video ([3341e88](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/3341e88c6204ff332a06aa420eac76bace574c22)) 112 | * del logo ([51c45da](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/51c45dab65a7f88789d90a55dae2d89e34e294a2)) 113 | * del video use gif ([cf5e76e](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/cf5e76e9a654a027ca209d949d997e3b9b213f45)) 114 | * fix error markdown ([f9a7c18](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/f9a7c1888ec70593d3034c2c8f8a15238dc8cbfa)) 115 | * fix key error command ([a095c7a](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/a095c7ac5e2cd072c6e7c352ce345b753ff09d3f)) 116 | * upgrate install style ([94a7cc2](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/94a7cc2fbb46e3b317c671aaec397bde2c3d794b)) 117 | * upgrate README.md ([7a772b0](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/7a772b085e7fc2417d0df016b2011f0579ab15ae)) 118 | * use mp4 ([d9675af](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/d9675afcf412ce7616d7d4f241a984e29080cde9)) 119 | 120 | 121 | ### Code Refactoring 122 | 123 | * very good code layout ([9a2ba38](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/9a2ba38f1677fb9ffd759679cfd9a98c463bc4e7)) 124 | 125 | 126 | ### CI 127 | 128 | * add issue template ([6947b08](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/6947b08480bc7df41023f56f9a9a989f6c40c9c2)) 129 | * fix bug_report template typo ([261fd48](https://github.com/jiangxue-analysis/nvim.comment-hide/commit/261fd488ee6af6606e7ea584c8cc4d7d97b7204a)) 130 | -------------------------------------------------------------------------------- /test/scala.scala: -------------------------------------------------------------------------------- 1 | import com.raquo.laminar.api.L.* 2 | 3 | import org.scalajs.dom 4 | import scala.annotation.tailrec 5 | import scala.meta.* 6 | import com.raquo.airstream.core.Signal 7 | 8 | case class TreeView( 9 | tree: Tree, 10 | textIndex: TextIndex, 11 | openNodes: Var[Set[Int]], 12 | cursor: Var[CodeMirrorCursor], 13 | hover: Var[Option[Int]] 14 | ): 15 | 16 | def getTree(id: Int): Option[Tree] = 17 | direct.get(id) 18 | 19 | lazy val node = div( 20 | cls := "bg-gray-100 p-2 rounded-lg shadow-md w-full", 21 | deepestTreeUnderCursor --> pathToCursor, 22 | pathToCursor.signal.map(_.toSet) --> append, 23 | div( 24 | cls := "flex flex-row gap-2 mb-2", 25 | a( 26 | href := "#", 27 | cls := "bg-gray-200 hover:bg-gray-300 rounded-md px-2 py-1 text-xs", 28 | "collapse all", 29 | onClick.mapToStrict(Set.empty) --> openNodes, 30 | onClick.mapToStrict(List.empty) --> pathToCursor 31 | ), 32 | a( 33 | href := "#", 34 | cls := "bg-gray-200 hover:bg-gray-300 rounded-md px-2 py-1 text-xs", 35 | "expand all", 36 | onClick.mapTo(direct.keySet) --> openNodes 37 | ), 38 | a( 39 | href := "#", 40 | cls := "bg-gray-200 hover:bg-gray-300 rounded-md px-2 py-1 text-xs", 41 | "copy", 42 | onClick --> { _ => 43 | openNodes.set(direct.keySet) 44 | dom.window.setTimeout( 45 | () => copyTreeStructure(reverse(tree)), 46 | 0 47 | ) 48 | } 49 | ) 50 | ), 51 | code(encode(tree)) 52 | ) 53 | 54 | /** Copy tree */ 55 | private def copyTreeStructure(id: Int): Unit = 56 | val treeOpt = direct.get(id) 57 | treeOpt.foreach { t => 58 | val structure = generateTreeStructure(t, id) 59 | dom.window.navigator.clipboard.writeText(structure) 60 | } 61 | 62 | /** Copy tree starting from selected node */ 63 | private def generateTreeStructure( 64 | t: Tree, 65 | currentId: Int, 66 | depth: Int = 0 67 | ): String = 68 | val prefix = " " * depth 69 | val isExpanded = openNodes.now().contains(currentId) 70 | val hasChildren = t.children.nonEmpty 71 | val symbol = 72 | if hasChildren then (if isExpanded then "- " else "+ ") else " " 73 | 74 | val nodeLine = 75 | s"$prefix$symbol${t.productPrefix} [${t.pos.start};${t.pos.end}]" 76 | 77 | if !hasChildren || !isExpanded then nodeLine 78 | else 79 | val childrenText = t.children 80 | .map { child => 81 | val childId = reverse(child) 82 | generateTreeStructure(child, childId, depth + 1) 83 | } 84 | .mkString("\n") 85 | 86 | s"$nodeLine\n$childrenText" 87 | end if 88 | end generateTreeStructure 89 | 90 | /** Expand/collapse a single tree by its id */ 91 | private val toggle = openNodes.updater[Int]: (cur, next) => 92 | if cur(next) then cur - next else cur + next 93 | 94 | /** Helper to expand all trees with a given Set[Int] of tree ids */ 95 | private val append = openNodes.updater[Set[Int]]: (cur, next) => 96 | cur ++ next 97 | 98 | /** True if tree is expanded in the tree view */ 99 | private def isToggled(id: Int) = openNodes.signal.map(_.contains(id)) 100 | 101 | /** Represents the path from the root to the deepest tree that spans the 102 | * current cursor position 103 | */ 104 | private val pathToCursor = Var(List.empty[Int]) 105 | 106 | /** Monitor cursor position and find the deepest tree that spans the cursor 107 | * position, returning a reversed list of ancestors (deepest first) of that 108 | * tree. 109 | */ 110 | private lazy val deepestTreeUnderCursor: Signal[List[Int]] = 111 | cursor.signal.map: cursor => 112 | textIndex 113 | .posLookup(cursor.line, cursor.ch) 114 | .map: offset => 115 | val deepest = intervalTree 116 | .resolve(offset) 117 | .sortBy: treeId => 118 | offset - direct(treeId).pos.start 119 | 120 | deepest.headOption match 121 | case None => Nil 122 | case Some(value) => 123 | @tailrec 124 | def go(id: Int, cur: List[Int]): List[Int] = 125 | direct.get(id).flatMap(_.parent).flatMap(reverse.get) match 126 | case None => cur 127 | case Some(parent) => go(parent, parent :: cur) 128 | 129 | go(value, List(value)).reverse 130 | end match 131 | .getOrElse(Nil) 132 | 133 | private lazy val ( 134 | /** Map from tree id to tree */ 135 | direct, 136 | /** Map from tree to id */ 137 | reverse 138 | ) = index(tree) 139 | 140 | private lazy val intervalTree = IntervalTree.construct(reverse.map: 141 | (tree, id) => OffsetRange(tree.pos.start, tree.pos.end) -> id) 142 | 143 | /** Recursively create DOM tree structure that represents the Scalameta tree. 144 | * 145 | * TODO: Make this tail recursive. 146 | */ 147 | private def encode(t: Tree): Element = 148 | val id = reverse(t) 149 | span( 150 | cls := "group", 151 | span( 152 | cls <-- pathToCursor.signal.map(p => 153 | if p.headOption.contains(id) then "bg-amber-500" 154 | else if p.contains(id) then "bg-amber-100" 155 | else "" 156 | ), 157 | a( 158 | cls := "text-blue-700 text-sm", 159 | href := "#", 160 | child.text <-- isToggled(id).map: b => 161 | val moniker = 162 | if t.children.isEmpty then " " else if b then "- " else "+ " 163 | moniker + t.productPrefix 164 | , 165 | onClick.preventDefault.mapToStrict(id) --> toggle, 166 | onMouseOver.mapToStrict(id) --> hover.someWriter, 167 | onMouseOut.mapToStrict(None) --> hover 168 | ), 169 | " ", 170 | i( 171 | cls := "text-amber-700 text-xs font-mono", 172 | s"[${t.pos.start};${t.pos.end}]" 173 | ) 174 | ), 175 | if t.children.nonEmpty then 176 | a( 177 | cls := "ml-2 text-gray-500 hover:text-gray-700 text-xs opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer", 178 | title := "Copy node structure", 179 | "[copy]", 180 | onClick.preventDefault --> { _ => 181 | openNodes.update(_ + id) 182 | dom.window.requestAnimationFrame { _ => 183 | copyTreeStructure(id) 184 | } 185 | } 186 | ) 187 | else emptyNode, 188 | ul( 189 | cls := "list-inside list-none ml-4", 190 | t.children.map(child => li(encode(child))), 191 | display <-- openNodes.signal 192 | .map(_.contains(id)) 193 | .map(if _ then "block" else "none") 194 | ) 195 | ) 196 | end encode 197 | 198 | /** Index every tree with a numeric identifier */ 199 | private def index(t: Tree): (Map[Int, Tree], Map[Tree, Int]) = 200 | def go( 201 | next: Seq[Tree], 202 | n: Int, 203 | direct: Map[Int, Tree], 204 | reverse: Map[Tree, Int] 205 | ): (Map[Int, Tree], Map[Tree, Int]) = 206 | if next.isEmpty then (direct, reverse) 207 | else 208 | val ids = next.zipWithIndex.map { case (tree, idx) => 209 | (n + idx) -> tree 210 | }.toMap 211 | val reverseIds = ids.map(_.swap).toMap 212 | go( 213 | next.flatMap(_.children), 214 | n + ids.size, 215 | direct ++ ids, 216 | reverse ++ reverseIds 217 | ) 218 | end go 219 | go(Seq(t), 0, Map.empty, Map.empty) 220 | end index 221 | end TreeView 222 | -------------------------------------------------------------------------------- /test/elixir.exs: -------------------------------------------------------------------------------- 1 | # Generate all files required for a practice exercise. 2 | # File content is filled as much as possible, but some, including tests, need manual input. 3 | # 4 | # Do not run directly, instead use this script: 5 | # $ bin/generate_practice_exercise.sh complex-numbers 6 | # Pass the name of the exercise (e. g., "complex-numbers") as an argument 7 | 8 | Mix.install([ 9 | {:jason, "~> 1.2"}, 10 | {:toml, "~> 0.6"} 11 | ]) 12 | 13 | defmodule Generate do 14 | def explore_properties(%{"cases" => cases}) when is_list(cases) do 15 | Enum.map(cases, &Generate.explore_properties/1) 16 | |> Enum.reduce( 17 | %{}, 18 | &Map.merge(&1, &2, fn _prop, p1, p2 -> %{p1 | error: p1.error or p2.error} end) 19 | ) 20 | end 21 | 22 | def explore_properties(%{"property" => property, "input" => input, "expected" => expected}), 23 | do: %{ 24 | property => %{ 25 | name: Macro.underscore(property), 26 | variables: 27 | if match?(%{}, input) do 28 | Enum.map(input, fn 29 | {var, %{} = val} -> 30 | {Macro.underscore(var), Enum.map(val, fn {v, _} -> Macro.underscore(v) end)} 31 | 32 | {var, _} -> 33 | {Macro.underscore(var), nil} 34 | end) 35 | else 36 | [{"input", nil}] 37 | end, 38 | error: match?(%{"error" => _err}, expected) 39 | } 40 | } 41 | 42 | def explore_properties(_data), do: %{} 43 | 44 | def print_property(%{name: name, variables: variables, error: error}) do 45 | return_type = if error, do: "{:ok, TODO.t()} | {:error, String.t()}", else: "TODO.t()" 46 | 47 | variable_types = 48 | Enum.map_join(variables, ", ", fn 49 | {var, nil} -> 50 | "#{var} :: TODO.t()" 51 | 52 | {var, sub_vars} -> 53 | "#{var} :: %{#{Enum.map_join(sub_vars, ", ", &(&1 <> ": TODO.t()"))}}" 54 | end) 55 | 56 | variable_list = Enum.map_join(variables, ", ", fn {var, _} -> var end) 57 | 58 | """ 59 | @doc \"\"\" 60 | TODO: add function description and replace types in @spec 61 | \"\"\" 62 | @spec #{name}(#{variable_types}) :: #{return_type} 63 | def #{name}(#{variable_list}) do 64 | end 65 | """ 66 | end 67 | 68 | def print_comments(comments, do_not_print) do 69 | comments 70 | |> Enum.reject(fn {field, _value} -> field in do_not_print end) 71 | |> Enum.map_join("\n", fn 72 | {"comments", values} when is_list(values) -> 73 | "# #{Enum.map_join(values, "\n# ", &String.trim/1)}" 74 | 75 | {field, values} when is_list(values) -> 76 | "#\n# --#{field} --\n# #{Enum.map_join(values, "\n# ", &inspect(&1, limit: :infinity))}" 77 | 78 | {field, value} -> 79 | "#\n# -- #{field} --\n# #{inspect(value, limit: :infinity)}" 80 | end) 81 | end 82 | 83 | def print_input(%{} = input), 84 | do: 85 | Enum.map_join(input, "\n", fn {variable, value} -> 86 | "#{Macro.underscore(variable)} = #{inspect(value, limit: :infinity)}" 87 | end) 88 | 89 | def print_input(input), do: "input = #{inspect(input, limit: :infinity)}" 90 | 91 | def print_expected(%{"error" => err}, _error), do: "{:error, #{inspect(err, limit: :infinity)}}" 92 | def print_expected(expected, true), do: "{:ok, #{inspect(expected, limit: :infinity)}}" 93 | def print_expected(expected, false), do: inspect(expected, limit: :infinity) 94 | 95 | def print_test_case( 96 | %{"description" => description, "cases" => sub_cases} = category, 97 | properties, 98 | module 99 | ) do 100 | """ 101 | describe \"#{description}\" do 102 | #{Generate.print_comments(category, ["description", "cases"])} 103 | #{Enum.map_join(sub_cases, "\n\n", &Generate.print_test_case(&1, properties, module))} 104 | end 105 | """ 106 | end 107 | 108 | def print_test_case( 109 | %{ 110 | "description" => description, 111 | "property" => property, 112 | "input" => input, 113 | "expected" => expected 114 | } = test, 115 | properties, 116 | module 117 | ) do 118 | %{name: name, variables: variables, error: error} = properties[property] 119 | variable_list = Enum.map_join(variables, ", ", fn {var, _} -> var end) 120 | 121 | """ 122 | @tag :pending 123 | test \"#{description}\" do 124 | #{Generate.print_comments(test, ["description", "property", "input", "expected", "uuid"])} 125 | #{print_input(input)} 126 | output = #{module}.#{name}(#{variable_list}) 127 | expected = #{print_expected(expected, error)} 128 | 129 | assert output == expected 130 | end 131 | """ 132 | end 133 | end 134 | 135 | [exercise] = System.argv() 136 | 137 | exercise_snake_case = String.replace(exercise, "-", "_") 138 | 139 | module = 140 | exercise 141 | |> String.split("-") 142 | |> Enum.map_join("", &String.capitalize/1) 143 | 144 | ## Step 1: create folder structure 145 | 146 | File.cd!("exercises/practice/#{exercise}") 147 | Mix.Generator.create_directory("lib") 148 | Mix.Generator.create_directory("test") 149 | 150 | ## Step 2: add common files 151 | 152 | # .formatter.exs 153 | format = """ 154 | # Used by "mix format" 155 | [ 156 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 157 | ] 158 | """ 159 | 160 | Mix.Generator.create_file(".formatter.exs", format) 161 | 162 | # mix.exs 163 | mix = """ 164 | defmodule #{module}.MixProject do 165 | use Mix.Project 166 | 167 | def project do 168 | [ 169 | app: :#{exercise_snake_case}, 170 | version: "0.1.0", 171 | start_permanent: Mix.env() == :prod, 172 | deps: deps() 173 | ] 174 | end 175 | 176 | # Run "mix help compile.app" to learn about applications. 177 | def application do 178 | [ 179 | extra_applications: [:logger] 180 | ] 181 | end 182 | 183 | # Run "mix help deps" to learn about dependencies. 184 | defp deps do 185 | [ 186 | # {:dep_from_hexpm, "~> 0.3.0"}, 187 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 188 | ] 189 | end 190 | end 191 | """ 192 | 193 | Mix.Generator.create_file("mix.exs", mix) 194 | 195 | # .gitignore 196 | gitignore = """ 197 | # The directory Mix will write compiled artifacts to. 198 | /_build/ 199 | 200 | # If you run "mix test --cover", coverage assets end up here. 201 | /cover/ 202 | 203 | # The directory Mix downloads your dependencies sources to. 204 | /deps/ 205 | 206 | # Where third-party dependencies like ExDoc output generated docs. 207 | /doc/ 208 | 209 | # Ignore .fetch files in case you like to edit your project deps locally. 210 | /.fetch 211 | 212 | # If the VM crashes, it generates a dump, let's ignore it too. 213 | erl_crash.dump 214 | 215 | # Also ignore archive artifacts (built via "mix archive.build"). 216 | *.ez 217 | 218 | # Ignore package tarball (built via "mix hex.build"). 219 | #{exercise_snake_case}-*.tar 220 | 221 | # Temporary files, for example, from tests. 222 | /tmp/ 223 | """ 224 | 225 | Mix.Generator.create_file(".gitignore", gitignore) 226 | 227 | # test/test_helper.exs 228 | test_helper = """ 229 | ExUnit.start() 230 | ExUnit.configure(exclude: :pending, trace: true) 231 | """ 232 | 233 | Mix.Generator.create_file("test/test_helper.exs", test_helper) 234 | 235 | ## Step 3: write files that depend on problem specifications 236 | 237 | url = 238 | ~c"https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/#{exercise}" 239 | 240 | :inets.start() 241 | :ssl.start() 242 | 243 | # tests and lib files 244 | {:ok, {_status, _header, data}} = 245 | :httpc.request(:get, {url ++ ~c"/canonical-data.json", []}, [], []) 246 | 247 | data = Jason.decode!(data) 248 | 249 | properties = Generate.explore_properties(data) 250 | 251 | # Generating lib file 252 | lib_file = """ 253 | defmodule #{module} do 254 | 255 | #{properties |> Map.values() |> Enum.map_join("\n\n", &Generate.print_property/1)} 256 | 257 | end 258 | """ 259 | 260 | path = "lib/#{exercise_snake_case}.ex" 261 | Mix.Generator.create_file(path, lib_file) 262 | 263 | Mix.Generator.copy_file(path, ".meta/example.ex") 264 | 265 | # Generating test file 266 | test_file = 267 | """ 268 | defmodule #{module}Test do 269 | use ExUnit.Case 270 | 271 | #{Generate.print_comments(data, ["cases", "exercise"])} 272 | #{Enum.map_join(data["cases"], "\n\n", &Generate.print_test_case(&1, properties, module))} 273 | end 274 | """ 275 | |> String.replace("@tag", "# @tag", global: false) 276 | 277 | path = "test/#{exercise_snake_case}_test.exs" 278 | Mix.Generator.create_file(path, test_file) 279 | 280 | # mix format all files 281 | Mix.Tasks.Format.run(["**/*.{ex,exs}"]) 282 | -------------------------------------------------------------------------------- /test/svelte.svelte: -------------------------------------------------------------------------------- 1 | 194 | 195 | {#if !ready && $UserStore.signedIn === false} 196 | 197 | {/if} 198 | 199 | 200 | {#if $UserStore.signedIn === true && !newDay} 201 | 202 | 203 | {:else if $UserStore.signedIn == undefined || newDay} 204 |
205 | 206 |
207 | {/if} 208 | 209 | 210 | {#if ready && $Interact.stats.terms.length} 211 | 212 | {/if} 213 | {#if ready && $TrackerLibrary.show} 214 | 215 | {/if} 216 | {#if ready && $Interact.people.active} 217 | 218 | {/if} 219 | {#if $Interact.blocker.show} 220 |
221 |
222 |
223 | {#if !$Interact.blocker.percent} 224 | 225 | {/if} 226 | {$Interact.blocker.message} 227 |
228 | {#if $Interact.blocker.percent} 229 | 230 | {/if} 231 |
232 |
233 | {/if} 234 | 235 | 236 | 237 | 238 | 239 | 240 | {#if $Interact.focusedEditor} 241 | 242 | {/if} 243 | 244 | {#if $Interact.confetti.show} 245 | 246 | {/if} 247 | 248 | {#if $UserStore.storageType == 'blockstack' && $Device.offline} 249 |
No connection to Blockstack.
250 | {/if} 251 |
252 | avatar-holder 253 |
254 | 255 | -------------------------------------------------------------------------------- /test/python.py: -------------------------------------------------------------------------------- 1 | """ 2 | PST/PFF File Parser Module 3 | 4 | This module provides functionality to parse Outlook PST/PFF files, 5 | extract emails, attachments, and metadata. 6 | """ 7 | 8 | # Standard library imports 9 | import sys 10 | import os 11 | 12 | # Add root directory to Python path for module imports 13 | if __name__ == "__main__": 14 | import sys, os 15 | _root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 16 | sys.path.append(_root_dir) 17 | 18 | # Third-party library for PST file parsing 19 | import pypff 20 | 21 | # Local utility imports 22 | from modules.app_email.lib.yjSysUtils import * 23 | from modules.app_email.EmailBoxClass import parseReceivedLine, makeEmailHeaderType, parseEmailHeader, get_content_type, decode_body 24 | 25 | /* 26 | PST File Parser Class 27 | 28 | Handles the parsing of PST files and extraction of email content, 29 | headers, and attachments. 30 | */ 31 | class TPST_Parser: 32 | def __init__(self, fileName): 33 | """ 34 | Initialize the PST parser with the target file. 35 | 36 | Args: 37 | fileName (str): Path to the PST file to be parsed 38 | """ 39 | self.fileName = fileName # PST file path 40 | self.messageCount = 0 # Total message counter 41 | self.__file = None # Internal file handle 42 | self.root = None # Root folder of PST file 43 | self.EmailBox = None # Reference to parent EmailBox 44 | 45 | def __del__(self): 46 | """Destructor to ensure proper file handle cleanup""" 47 | if self.__file: self.__file.close() 48 | pass 49 | 50 | def open(self): 51 | """Open and initialize the PST file for reading""" 52 | self.__file = pypff.file() 53 | f = self.__file 54 | f.open(self.fileName) # Open the PST file 55 | self.root = f.get_root_folder() # Get root folder of PST 56 | 57 | def close(self): 58 | """Close the PST file handle""" 59 | self.__file.close() 60 | self.__file = None 61 | 62 | def traverseFolder(self, messageProc, base=None, _dir=''): 63 | """ 64 | Recursively traverse PST folder structure and process messages. 65 | 66 | Args: 67 | messageProc: Callback function to process each message 68 | base: Starting folder (defaults to root) 69 | _dir: Current directory path for tracking 70 | 71 | Returns: 72 | int: Total number of messages processed 73 | """ 74 | result = 0 75 | if not base: base = self.root 76 | 77 | # Process all subfolders recursively 78 | for folder in base.sub_folders: 79 | if folder.number_of_sub_folders: 80 | result += self.traverseFolder(messageProc, folder, os.path.join(_dir, folder.name)) 81 | 82 | # Process messages in current folder 83 | cnt = len(folder.sub_messages) 84 | result += cnt 85 | 86 | if messageProc and cnt: 87 | i = 0 88 | d = '%s/%s' % (_dir, folder.name) 89 | for message in folder.sub_messages: 90 | i += 1 91 | if not messageProc(self, i, message, d, cnt): break 92 | return result 93 | 94 | # Global counter for folder numbering 95 | _folder_no = 0 96 | 97 | # Constants for PST file properties 98 | value_Null_terminated_String = 0x001E # Property type for null-terminated strings 99 | value_Unicode_string = 0x001F # Property type for Unicode strings 100 | entry_Attachment_Filename = 0x3704 # Property ID for attachment filename 101 | entry_Attachement_method = 0x3705 # Property ID for attachment method 102 | entry_Attachment_Filename_long = 0x3707 # Property ID for long attachment filename 103 | 104 | def msgProc(self, no, message, _dir, cnt): 105 | """ 106 | Process individual email message and extract content. 107 | 108 | Args: 109 | self: TPST_Parser instance 110 | no: Message number in current folder 111 | message: The email message object 112 | _dir: Directory path within PST 113 | cnt: Total messages in current folder 114 | 115 | Returns: 116 | bool: True to continue processing, False to stop 117 | """ 118 | 119 | def getBody(message, charset): 120 | """ 121 | Extract message body content with proper character encoding. 122 | 123 | Args: 124 | message: Email message object 125 | charset: Character encoding to use 126 | 127 | Returns: 128 | str: Decoded message body 129 | """ 130 | data = message.get_html_body() 131 | if data == None: 132 | data = message.get_plain_text_body() 133 | if data == None: return '' 134 | if __debug__: type(data) is bytes 135 | return decode_body(data, charset) 136 | 137 | global _folder_no 138 | if no == 1: _folder_no += 1 # Increment folder counter for first message 139 | if __debug__: assert ExtractFilePath(self.fileName) != '' 140 | 141 | # Initialize message info dictionary 142 | msg_info = {} 143 | msg_info['file'] = ExtractFileName(self.fileName) 144 | msg_info['dir'] = _dir 145 | 146 | # Process email headers 147 | headers = message.get_transport_headers() 148 | if headers == None: return True # Skip if no headers 149 | 150 | headers = parseEmailHeader(headers) 151 | content_type = get_content_type(headers) 152 | if len(content_type) >= 2: 153 | charset = content_type[1] # Get character encoding from headers 154 | else: 155 | charset = 'utf-8' # Default to UTF-8 156 | 157 | # Build email message object 158 | clsEmailMessageObject = {} 159 | msg_info['EmailMessageObject'] = clsEmailMessageObject 160 | clsEmailMessageObject['Header'] = makeEmailHeaderType(headers) 161 | clsEmailMessageObject['Body'] = getBody(message, charset) 162 | 163 | # Process attachments if present 164 | if ('attachments' in dir(message)) and (message.number_of_attachments > 0): 165 | 166 | def attachment_name(attachment): 167 | """ 168 | Extract attachment filename from record sets. 169 | 170 | Args: 171 | attachment: Attachment object 172 | 173 | Returns: 174 | str: Attachment filename or None if not found 175 | """ 176 | try: 177 | if attachment.number_of_record_sets > 0: 178 | for entry in attachment.get_record_set(0).entries: 179 | if (entry.entry_type == entry_Attachment_Filename_long) and ( 180 | entry.value_type in [value_Null_terminated_String, value_Unicode_string]): 181 | return entry.data_as_string 182 | except: 183 | pass 184 | return None 185 | 186 | attachments = [] 187 | for i in range(message.number_of_attachments): 188 | attachment = message.get_attachment(i) 189 | if __debug__: assert type(attachment) is pypff.attachment 190 | name = attachment_name(attachment) 191 | if name == None: continue 192 | 193 | # Collect attachment info 194 | file_info = {} 195 | file_info['name'] = name # Attachment filename 196 | file_info['size'] = attachment.get_size() # Attachment size 197 | attachments.append(file_info) 198 | 199 | # Save attachment to disk 200 | try: 201 | f = open(os.path.join(self.EmailBox.createAttachmentDir(_folder_no, no), name), 'wb') 202 | f.write(attachment.read_buffer(attachment.get_size())) 203 | f.close() 204 | except Exception as e: 205 | continue # Skip failed attachments 206 | #print(e) 207 | 208 | if len(attachments) > 0: 209 | clsEmailMessageObject['Attachments'] = attachments 210 | #print(attachments) 211 | 212 | # Store processed message info 213 | # self.EmailBox.makeMessageFile(_folder_no, no, msg_info) # make json 214 | self.EmailBox.result.append(msg_info) 215 | return True 216 | 217 | def main(self): 218 | """ 219 | Main entry point for PST file processing. 220 | 221 | Args: 222 | self: EmailBox instance 223 | 224 | Returns: 225 | list: Processed email messages with metadata 226 | """ 227 | fileName = self.fileName 228 | pstParser = TPST_Parser(fileName) 229 | pstParser.EmailBox = self # Set parent EmailBox reference 230 | pstParser.open() # Open PST file 231 | 232 | # Process all messages in PST 233 | pstParser.traverseFolder(msgProc) 234 | pstParser.close() # Close PST file 235 | 236 | return self.result # Return processed results 237 | 238 | # Module execution handling 239 | if __name__ == "__main__": 240 | # Print libpff version when run directly 241 | print('libpff version :', pypff.get_version()) 242 | else: 243 | # Register main function with EmailBox class when imported 244 | from modules.app_email.EmailBoxClass import EmailBox 245 | EmailBox.main = main 246 | -------------------------------------------------------------------------------- /test/html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Global Digital Clock 7 | 148 | 149 | 150 |
151 | 152 |
153 |

Global Digital Clock

154 |

Showing current time with multiple timezones

155 |
156 | 157 | 158 |
00:00:00
159 | 160 | 161 |
Sunday, January 1, 2023
162 | 163 | 164 |
165 | 166 | 167 |
168 | 169 | 170 |
171 |
172 |

Tokyo

173 |
00:00:00
174 |
175 |
176 |

London

177 |
00:00:00
178 |
179 |
180 |

New York

181 |
00:00:00
182 |
183 |
184 | 185 | 186 |
187 |

Current local time: Automatically detected from your browser

188 |
189 |
190 | 191 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /test/vue.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 126 | 127 | 405 | -------------------------------------------------------------------------------- /test/clojure.cljc: -------------------------------------------------------------------------------- 1 | (ns app.common.logic.variants 2 | (:require 3 | [app.common.data :as d] 4 | [app.common.files.changes-builder :as pcb] 5 | [app.common.files.helpers :as cfh] 6 | [app.common.files.variant :as cfv] 7 | [app.common.logic.libraries :as cll] 8 | [app.common.logic.shapes :as cls] 9 | [app.common.logic.variant-properties :as clvp] 10 | [app.common.types.component :as ctk] 11 | [app.common.types.container :as ctn] 12 | [app.common.types.file :as ctf] 13 | [app.common.types.variant :as ctv] 14 | [app.common.uuid :as uuid])) 15 | 16 | (defn generate-add-new-variant 17 | [changes shape variant-id new-component-id new-shape-id prop-num] 18 | (let [data (pcb/get-library-data changes) 19 | objects (pcb/get-objects changes) 20 | component-id (:component-id shape) 21 | value (str ctv/value-prefix 22 | (-> (cfv/extract-properties-values data objects variant-id) 23 | last 24 | :value 25 | count 26 | inc)) 27 | 28 | [new-shape changes] (-> changes 29 | (cll/generate-duplicate-component 30 | {:data data} 31 | component-id 32 | new-component-id 33 | {:new-shape-id new-shape-id :apply-changes-local-library? true}))] 34 | (-> changes 35 | (clvp/generate-update-property-value new-component-id prop-num value) 36 | (pcb/change-parent (:parent-id shape) [new-shape] 0)))) 37 | 38 | (defn- generate-path 39 | [path objects base-id shape] 40 | (let [get-type #(case % 41 | :frame :container 42 | :group :container 43 | :rect :shape 44 | :circle :shape 45 | :bool :shape 46 | :path :shape 47 | %)] 48 | (if (= base-id (:id shape)) 49 | path 50 | (generate-path (str path " " (:name shape) (get-type (:type shape))) objects base-id (get objects (:parent-id shape)))))) 51 | 52 | (defn- add-unique-path 53 | "Adds a new property :shape-path to the shape, with the path of the shape. 54 | Suffixes like -1, -2, etc. are added to ensure uniqueness." 55 | [shapes objects base-id] 56 | (letfn [(unique-path [shape counts] 57 | (let [path (generate-path "" objects base-id shape) 58 | num (get counts path 1)] 59 | [(str path "-" num) (update counts path (fnil inc 1))]))] 60 | (first 61 | (reduce 62 | (fn [[result counts] shape] 63 | (let [[shape-path counts'] (unique-path shape counts)] 64 | [(conj result (assoc shape :shape-path shape-path)) counts'])) 65 | [[] {}] 66 | shapes)))) 67 | 68 | 69 | (defn- keep-swapped-item 70 | "As part of the keep-touched process on a switch, given a child on the original 71 | copy that was swapped (orig-swapped-child), and its related shape on the new copy 72 | (related-shape-in-new), move the orig-swapped-child into the parent of 73 | related-shape-in-new, fix its swap-slot if needed, and then delete 74 | related-shape-in-new" 75 | [changes related-shape-in-new orig-swapped-child ldata page swap-ref-id] 76 | (let [;; Before to the swap, temporary move the previous 77 | ;; shape to the root panel to avoid problems when 78 | ;; the previous parent is deleted. 79 | before-changes (-> (pcb/empty-changes) 80 | (pcb/with-page page) 81 | (pcb/with-objects (:objects page)) 82 | (pcb/change-parent uuid/zero [orig-swapped-child] 0 {:allow-altering-copies true})) 83 | 84 | objects (pcb/get-objects changes) 85 | prev-swap-slot (ctk/get-swap-slot orig-swapped-child) 86 | current-parent (get objects (:parent-id related-shape-in-new)) 87 | pos (d/index-of (:shapes current-parent) (:id related-shape-in-new))] 88 | 89 | 90 | (-> (pcb/concat-changes before-changes changes) 91 | 92 | ;; Move the previous shape to the new parent 93 | (pcb/change-parent (:parent-id related-shape-in-new) [orig-swapped-child] pos {:allow-altering-copies true}) 94 | 95 | ;; We need to update the swap slot only when it pointed 96 | ;; to the swap-ref-id. Oterwise this is a swapped item 97 | ;; inside a nested copy, so we need to keep it. 98 | (cond-> 99 | (= prev-swap-slot swap-ref-id) 100 | (pcb/update-shapes 101 | [(:id orig-swapped-child)] 102 | #(-> % 103 | (ctk/remove-swap-slot) 104 | (ctk/set-swap-slot (:shape-ref related-shape-in-new))))) 105 | 106 | ;; Delete new non-swapped item 107 | (cls/generate-delete-shapes ldata page objects (d/ordered-set (:id related-shape-in-new)) {:allow-altering-copies true}) 108 | second))) 109 | 110 | (defn- child-of-swapped? 111 | "Check if any ancestor of a shape (between base-parent-id and shape) was swapped" 112 | [shape objects base-parent-id] 113 | (let [ancestors (->> (ctn/get-parent-heads objects shape) 114 | ;; Ignore ancestors ahead of base-parent 115 | (drop-while #(not= base-parent-id (:id %))) 116 | seq) 117 | num-ancestors (count ancestors) 118 | ;; Ignore first and last (base-parent and shape) 119 | ancestors (when (and ancestors (<= 3 num-ancestors)) 120 | (subvec (vec ancestors) 1 (dec num-ancestors)))] 121 | (some ctk/get-swap-slot ancestors))) 122 | 123 | 124 | (defn generate-keep-touched 125 | "This is used as part of the switch process, when you switch from 126 | an original-shape to a new-shape. It generate changes to 127 | copy the touched attributes on the shapes children of the original-shape 128 | into the related children of the new-shape. 129 | This relation is tricky. The shapes are related if: 130 | * On the main components, both have the same name (the name on the copies are ignored) 131 | * Both has the same type of ancestors, on the same order (see generate-path for the 132 | translation of the types)" 133 | [changes new-shape original-shape original-shapes page libraries ldata] 134 | (let [objects (pcb/get-objects changes) 135 | container (ctn/make-container page :page) 136 | page-objects (:objects page) 137 | 138 | ;; Get the touched children of the original-shape 139 | ;; Ignore children of swapped items, because 140 | ;; they will be moved without change when 141 | ;; managing their swapped ancestor 142 | orig-touched (->> (filter (comp seq :touched) original-shapes) 143 | (remove 144 | #(child-of-swapped? % 145 | page-objects 146 | (:id original-shape)))) 147 | 148 | ;; Adds a :shape-path attribute to the children of the new-shape, 149 | ;; that contains the type of its ancestors and its name 150 | new-shapes-w-path (add-unique-path 151 | (reverse (cfh/get-children-with-self objects (:id new-shape))) 152 | objects 153 | (:id new-shape)) 154 | ;; Creates a map to quickly find a child of the new-shape by its shape-path 155 | new-shapes-map (into {} (map (juxt :shape-path identity)) new-shapes-w-path) 156 | 157 | ;; The original-shape is in a copy. For the relation rules, we need the referenced 158 | ;; shape on the main component 159 | orig-ref-shape (ctf/find-ref-shape nil container libraries original-shape) 160 | 161 | orig-ref-objects (-> (ctf/get-component-container-from-head orig-ref-shape libraries) 162 | :objects) 163 | 164 | ;; Adds a :shape-path attribute to the children of the orig-ref-shape, 165 | ;; that contains the type of its ancestors and its name 166 | o-ref-shapes-wp (add-unique-path 167 | (reverse (cfh/get-children-with-self orig-ref-objects (:id orig-ref-shape))) 168 | orig-ref-objects 169 | (:id orig-ref-shape)) 170 | 171 | ;; Creates a map to quickly find a child of the orig-ref-shape by its shape-path 172 | o-ref-shapes-p-map (into {} (map (juxt :id :shape-path)) o-ref-shapes-wp) 173 | 174 | 175 | ;; Process each touched children of the original-shape 176 | [changes parents-of-swapped] 177 | (reduce 178 | (fn [[changes parent-of-swapped] orig-child-touched] 179 | (let [;; If the orig-child-touched was swapped, get its swap-slot 180 | swap-slot (ctk/get-swap-slot orig-child-touched) 181 | 182 | ;; orig-child-touched is in a copy. Get the referenced shape on the main component 183 | ;; If there is a swap slot, we will get the referenced shape in another way 184 | orig-ref-shape (when-not swap-slot 185 | ;; TODO Maybe just get it from o-ref-shapes-wp 186 | (ctf/find-ref-shape nil container libraries orig-child-touched)) 187 | 188 | orig-ref-id (if swap-slot 189 | ;; If there is a swap slot, find the referenced shape id 190 | (ctf/find-ref-id-for-swapped orig-child-touched container libraries) 191 | ;; If there is not a swap slot, get the id from the orig-ref-shape 192 | (:id orig-ref-shape)) 193 | 194 | ;; Get the shape path of the referenced main 195 | shape-path (get o-ref-shapes-p-map orig-ref-id) 196 | ;; Get its related shape in the children of new-shape: the one that 197 | ;; has the same shape-path 198 | related-shape-in-new (get new-shapes-map shape-path) 199 | parents-of-swapped (if related-shape-in-new 200 | (conj parent-of-swapped (:parent-id related-shape-in-new)) 201 | parent-of-swapped) 202 | ;; If there is a related shape, keep its data 203 | changes 204 | (if related-shape-in-new 205 | (if swap-slot 206 | ;; If the orig-child-touched was swapped, keep it 207 | (keep-swapped-item changes related-shape-in-new orig-child-touched 208 | ldata page orig-ref-id) 209 | ;; If the orig-child-touched wasn't swapped, copy 210 | ;; the touched attributes into it 211 | (cll/update-attrs-on-switch 212 | changes related-shape-in-new orig-child-touched 213 | new-shape original-shape orig-ref-shape container)) 214 | changes)] 215 | [changes parents-of-swapped])) 216 | [changes []] 217 | orig-touched)] 218 | [changes parents-of-swapped])) 219 | -------------------------------------------------------------------------------- /test/r.r: -------------------------------------------------------------------------------- 1 | ## ############################################################################# 2 | ## basic math operations 3 | ## ############################################################################# 4 | 5 | 1 + 3 6 | 3 * 3 7 | 3^2 8 | ## ~undocumented, but works like in Python 9 | 3**2 10 | 11 | ## ############################################################################# 12 | ## Happy New Year in 2025! 13 | ## ############################################################################# 14 | 15 | 45^2 16 | 17 | ## code style: space before and after operators, except 18 | (20+25)^2 19 | 20 | (2+0+2+5) * (2-0-2-5) 21 | ((2+0+2+5) * (2-0-2-5))^2 22 | 23 | 1^3 + 2^3 + 3^3 + 4^3 + 5^3 + 6^3 + 7^3 + 8^3 + 9^3 24 | 1:9 25 | (1:9)^3 26 | sum((1:9)^3) 27 | 28 | sum(1:9)^2 29 | 30 | # we know vectors! do we? 31 | as.Date("2025-01-01") 32 | as.Date("2025-01-03") 33 | as.Date("2025-01-01"):as.Date("2025-01-03") 34 | 35 | str(as.Date("2025-01-01")) 36 | str(as.Date("2025-01-01"):as.Date("2025-01-03")) 37 | 38 | seq(as.Date("2025-01-01"), as.Date("2025-01-03"), by = "1 day") 39 | str(seq(as.Date("2025-01-01"), as.Date("2025-01-03"), by = "1 day")) 40 | 41 | weekdays(seq(as.Date("2025-01-01"), as.Date("2025-01-03"), by = "1 day")) 42 | substr(weekdays(seq(as.Date("2025-01-01"), as.Date("2025-01-03"), by = "1 day")), 1, 1) 43 | 44 | ## ############################################################################# 45 | ## constants 46 | ## ############################################################################# 47 | 48 | pi 49 | "pi" 50 | 'pi' 51 | 52 | ?pi 53 | 54 | ## ############################################################################# 55 | ## vectors recap 56 | ## ############################################################################# 57 | 58 | letters 59 | LETTERS 60 | str(letters) 61 | 62 | ## one-based indexing! 63 | letters[1] 64 | letters[5] 65 | letters[1:5] 66 | 1:5 67 | seq(1, 5) 68 | ?seq 69 | seq(1, 5, by = 0.1) 70 | seq(1, 5, 0.1) 71 | seq(by = 0.1, from = 1, to = 5) 72 | 73 | ## ############################################################################# 74 | ## variables 75 | ## ############################################################################# 76 | 77 | x = 4 # works, but not in sync with the common code style 78 | x <- 4 # good! 79 | 80 | x * 2 81 | 82 | ## TODO compute the square root of x 83 | x ^ 2 84 | x ^ 0.5 85 | 86 | ## ############################################################################# 87 | ## functions 88 | ## ############################################################################# 89 | 90 | sqrt(x) 91 | 92 | hist(runif(100000)) 93 | ?runif 94 | 95 | runif(5, 5, 25) 96 | 97 | ## TODO sine wave 98 | x <- 1:20 99 | x <- 'foobar' 100 | x <- runif 101 | x(5) 102 | x <- 1:20 103 | sin(x) 104 | 105 | ## ############################################################################# 106 | ## basic plots 107 | ## ############################################################################# 108 | 109 | plot(x, sin(x)) 110 | plot(x, sin(x), type = 'l') 111 | ?plot 112 | ?plot.default 113 | 114 | ## later => ggplot2 115 | 116 | x <- seq(0, pi*2, by = 0.1) 117 | plot(x, sin(x), type = 'l', col = 'red') 118 | 119 | curve(sin, to = pi * 2) 120 | ?curve 121 | ?plot 122 | ?plot.default 123 | curve(cos, to = pi * 2, col = 'red', add = TRUE) 124 | 125 | ## NOTE TRUE is often abbreviated to T, but avoid by all means, see e.g. 126 | ## T <- FALSE 127 | 128 | ## TODO Brownian motion / random walk for 100 steps 129 | x <- 0 130 | # +1 131 | # -1 132 | 133 | for (i in 1:100) { # whitespace doesn't matter, but stick with a code style! 134 | if (runif(1) < 0.5) { 135 | x <- x - 1 136 | } else { 137 | x <- x + 1 138 | } 139 | } 140 | x 141 | 142 | set.seed(42) 143 | x <- sample(c(1, -1), 144 | size = 100, 145 | replace = TRUE) 146 | 147 | cumsum(x) 148 | plot(cumsum(x), type = 's') 149 | 150 | ## vectorize (always) 151 | x <- round(runif(100))*2-1 152 | cumsum(x) 153 | 154 | w <- matrix(round(runif(1e3))*2-1, nrow = 10) 155 | w <- apply(w, 1, cumsum) 156 | w 157 | w[100, ] 158 | 159 | w <- matrix(round(runif(1e6))*2-1, nrow = 1e3) 160 | w <- apply(w, 1, cumsum) 161 | str(w) 162 | w[1000, ] 163 | hist(w[1000, ]) 164 | 165 | w <- matrix(round(runif(1e3))*2-1, nrow = 100) 166 | w <- apply(w, 1, sum) 167 | 168 | 169 | ## DATA! 170 | h <- c(174, 170, 160) 171 | w <- c(90, 80, 70) 172 | 173 | plot(h, w, main = 'Heights and weights of students', 174 | xlab = 'Height', ylab = 'Weight') 175 | 176 | ## ############################################################################# 177 | ## basic stats 178 | ## ############################################################################# 179 | 180 | min(w) 181 | max(w) 182 | range(w) 183 | diff(range(w)) 184 | mean(w) 185 | median(w) 186 | sum(w) 187 | summary(w) 188 | 189 | cor(w, h) 190 | lm(w ~ h) # linear model 191 | 192 | 193 | 165 * 1.3462 -146.1538 194 | 195 | fit <- lm(w ~ h) 196 | predict(fit, newdata = list(h = 165)) 197 | 198 | ## extrapolation + needs more data! e.g. on newborns.. 199 | predict(fit, newdata = list(h = 52)) 200 | 201 | str(fit) 202 | fit 203 | summary(fit) 204 | 205 | abline(fit, col = 'red') 206 | 207 | ## ############################################################################# 208 | ## data frames 209 | ## ############################################################################# 210 | 211 | df <- data.frame(weight = w, height = h) 212 | df 213 | str(df) 214 | df$weight 215 | df$weight[1] 216 | df[1, 1] 217 | df[2, 2] 218 | df[1, ] 219 | df[, 1] 220 | 221 | cor(df) 222 | plot(df) 223 | lm(df) 224 | 225 | ## compute Body Mass Index (BMI) 226 | df$bmi <- df$weight / (df$height/100)^2 227 | str(df) 228 | df 229 | 230 | df <- read.csv('http://bit.ly/CEU-R-heights') 231 | str(df) 232 | plot(df) 233 | 234 | ## TODO check the number of observation 235 | length(df) 236 | str(df) 237 | nrow(df) 238 | ?nrow 239 | dim(df) 240 | 241 | ## dim returns same as 242 | c(nrow(df), ncol(df)) 243 | 244 | ## TODO check the height of the 2nd respondent 245 | df$heightIn[2] 246 | df[2, 4] 247 | df[2, 'heightIn'] 248 | df[2, "heightIn"] 249 | 250 | ## TODO compute height in cm 251 | df$heightIn * 2.54 252 | str(df) 253 | df$height <- df$heightIn * 2.54 254 | str(df) 255 | 256 | ## drop a column 257 | df$heightIn <- NULL 258 | str(df) 259 | 260 | ## TODO compute weight in kg 261 | df$weight <- df$weightLb * .45 262 | str(df) 263 | 264 | ## TODO compute BMI 265 | df$bmi <- df$weight / (df$height/100)^2 266 | 267 | ## removing 2 columns 268 | df$weightLb <- df$ageMonth <- NULL 269 | str(df) 270 | 271 | ## quick EDA 272 | plot(df) ## data.frame 273 | 274 | ## interactivity 275 | install.packages('pairsD3') 276 | library(pairsD3) 277 | pairsD3(df) 278 | 279 | ## why ggplot? 280 | install.packages('GGally') 281 | library(GGally) 282 | ggpairs(df) 283 | 284 | ## ############################################################################# 285 | ## intro to ggplot2 286 | ## ############################################################################# 287 | 288 | ## the R implementation of the Grammar of Graphics 289 | library(ggplot2) 290 | 291 | ## barplot with ggplot: you specify a dataset, then define an aesthetic and geom 292 | ggplot(df, aes(x = height)) + geom_histogram() 293 | ## and optionally some further layers (theme) 294 | ggplot(df, aes(x = height)) + geom_histogram() + theme_bw() 295 | ggplot(df, aes(x = height)) + geom_histogram(colour = "darkgreen", fill = "white") + theme_bw() 296 | 297 | ## we can store the resulting plot as an R object for future reuse 298 | p <- ggplot(df, aes(x = height)) + geom_histogram() 299 | p 300 | p + theme_bw() 301 | 302 | ## it's better to split long lines for easier reading 303 | p <- ggplot(df, aes(x = height)) + 304 | geom_histogram() + 305 | theme_bw() 306 | p 307 | 308 | ## coord transformations 309 | library(scales) 310 | p + scale_y_log10() 311 | p + scale_y_sqrt() 312 | p + scale_y_reverse() 313 | p + coord_flip() 314 | 315 | ## other categorical variable: country 316 | ggplot(df, aes(x = sex)) + geom_histogram() # ERROR 317 | ggplot(df, aes(x = sex)) + geom_bar() 318 | ggplot(df, aes(x = sex)) + geom_bar() + theme(axis.text.x = element_text(angle = 90)) 319 | ## QQ ordering? later... 320 | ?theme 321 | 322 | ## other geoms 323 | ggplot(df, aes(height, weight)) + geom_point() 324 | ggplot(df, aes(height, weight)) + geom_point() + geom_smooth() 325 | ggplot(df, aes(height, weight)) + geom_point() + geom_smooth(method = 'lm', color = 'red', se = FALSE) 326 | 327 | ## mix continious with a discrete variable 328 | ggplot(df, aes(height)) + geom_boxplot() 329 | ggplot(df, aes(sex, height)) + geom_boxplot() 330 | ggplot(df, aes(sex, height)) + geom_violin() 331 | ggplot(df, aes(sex, height)) + geom_boxplot() + geom_violin(alpha = .25) 332 | ggplot(df, aes(sex, height)) + geom_boxplot() + geom_violin(alpha = .25) + geom_jitter() 333 | 334 | ## facet => create separate plots per group 335 | p <- ggplot(df, aes(x = height)) + geom_histogram() 336 | p + facet_wrap( ~ sex) 337 | 338 | ## density plot 339 | ggplot(df, aes(x = height)) + geom_density() 340 | ggplot(df, aes(x = height, fill = sex)) + geom_density() 341 | ggplot(df, aes(x = height, fill = sex)) + geom_density(alpha = 0.2) + theme_bw() 342 | ggplot(df, aes(x = height, fill = sex)) + geom_density(alpha = 0.2) + theme_bw() + 343 | ggtitle("Height of boys and girls") + 344 | xlab("Height (cm)") + ylab("") + 345 | theme(legend.position = "top") 346 | 347 | ?theme 348 | 349 | ## plot number of girls and boys below and above 160 cm! 350 | ## maybe a stacked barchart? 351 | 352 | ggplot(df, aes(x = sex, y = ...)) + geom_bar() 353 | ## need to transform the data ... 354 | df$height_cat <- df$height < 160 355 | table(df$height_cat) 356 | df$height_cat <- cut(df$height, breaks = c(0, 160, Inf)) 357 | table(df$height_cat) 358 | 359 | ggplot(df, aes(x = sex, fill = height_cat)) + geom_bar() 360 | ggplot(df, aes(x = sex, fill = height_cat)) + geom_bar(position = "dodge") 361 | ggplot(df, aes(x = sex, fill = height_cat)) + geom_bar(position = "fill") 362 | 363 | ## ############################################################################# 364 | ## intro to data.table 365 | ## ############################################################################# 366 | 367 | ## avg height per gender 368 | mean(df[df$sex == "f", "weight"]) 369 | 370 | dff <- subset(df, sex == 'f') 371 | mean(dff$weight) 372 | x 373 | aggregate(height ~ sex, FUN = mean, data = df) 374 | 375 | ## there must be a better way! 376 | library(data.table) 377 | dt <- data.table(df) 378 | dt # VS df! 379 | 380 | ## dt[i] 381 | dt[1] 382 | dt[1:5] 383 | dt[sex == "f"] 384 | dt[sex == "f"][1:5] # chaining 385 | dt[ageYear == min(ageYear)] 386 | dt[ageYear == min(ageYear)][order(height)] 387 | 388 | dt[round(runif(1)*.N)] 389 | dt[round(runif(10)*.N)] 390 | dt[.N] 391 | 392 | ## dt[i, j] 393 | dt[, mean(height)] 394 | dt[ageYear == min(ageYear), mean(height)] 395 | dt[ageYear == min(ageYear), summary(height)] 396 | dt[ageYear == min(ageYear), hist(height)] 397 | 398 | ## TODO compute the average height of girls and boys 399 | dt[sex == "f", mean(height)] 400 | dt[sex == "m", mean(height)] 401 | 402 | ## dt[i, j, by] 403 | dt[, mean(height), by = sex] 404 | dt[, list(H = mean(height)), by = list(gender = sex)] 405 | ## note that list can be abbreviated by a dot (.) in data.table 406 | dt[, .(H = mean(height)), by = .(gender = sex)] 407 | dt[, .(H = mean(height), W = mean(weight)), by = .(gender = sex)] 408 | dt[, .(H = mean(height), W = mean(weight)), by = .(gender = sex, elementary_school = ageYear < 14)] 409 | 410 | ## count the number of folks below/above 12 yrs 411 | dt$agecat <- cut(dt$ageYear, c(0, 12, Inf)) 412 | dt[, agecat := cut(ageYear, c(0, 12, Inf))] 413 | dt[, .N, by = agecat] 414 | ## show the average weight of high BMI (25) folks 415 | dt[bmi > 25, mean(weight)] 416 | ## categorize folks to underweight (<18.5)/normal/overweight (25+) 417 | dt[, bmicat := cut(bmi, c(0, 18.5, 25, Inf))] 418 | ## stacked bar chart for BMI categorization split by gender 419 | ggplot(dt, aes(x = sex, fill = bmicat)) + geom_bar() 420 | ggplot(dt[, .N, by = .(sex, bmicat)], aes(x = sex, y = N, fill = bmicat)) + geom_col() 421 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### nvim.command-hide 2 | 3 | The plugin allows you to hide and show comments, and saves them to a specified folder. 4 | 5 | ![](demo.gif) 6 | 7 | #### Why install? 8 | 9 | > [!NOTE] 10 | > This is test version, if error and bug, click [issues](https://github.com/jiangxue-analysis/nvim.comment-hide/issues). 11 | 12 | You are use [lazy.nvim](https://github.com/folke/lazy.nvim): 13 | 14 | ```lua 15 | 16 | return { 17 | "my-rubbish/nvim.comment-hide", 18 | name = "comment-hide", 19 | lazy = false, 20 | config = function() 21 | require("comment-hide").setup({ 22 | gitignore = true, -- Automatically add .annotations/ to .gitignore. 23 | }) 24 | vim.keymap.set("n", "vs", "CommentHideSave", { desc = "Comment: Save (strip comments)" }) 25 | vim.keymap.set("n", "vr", "CommentHideRestore", { desc = "Comment: Restore from backup" }) 26 | end, 27 | } 28 | ``` 29 | 30 | If you not user [lazy.nvim](https://github.com/folke/lazy.nvim)? God be with you~ 31 | 32 | #### Why use? 33 | 34 | 1. **:CommentHideSave**: Create `.annotations/` storage code comments and **Delete the current file comment** move comments to `.annotations`. 35 | 2. **:CommentHideRestore**: Restore comments from `.annotations/` to the current file. 36 | 37 | If you add the `.annotations/` directory to the `.gitignore` file, anyone without this directory will **be unable to restore your comments**. 38 | 39 | #### Public comments 40 | 41 | > 42 | > 43 | > After executing `:CommentHideSave`, **please do not make any changes**, as this will disrupt the line numbers and prevent `:CommentHideRestore` from restoring the comments. 👊🐱🔥 44 | 45 | ```js 46 | 0 /* >>> 47 | 1 This will not be hidden and will be 2 visible to everyone 48 | 2 */ 49 | 3 50 | 4 const x = 42; // This is a comment 51 | 5 /* This is a multi-line 52 | 6 comment */ 53 | 7 // Another comment 54 | ``` 55 | 56 | run `:CommentHideRestore`: 57 | 58 | ```js 59 | 1 /* >>> 60 | 2 This will not be hidden and will be 3 visible to everyone 61 | 3 */ 62 | 4 63 | 5 const x = 42; 64 | ``` 65 | 66 | The `/* */` block remains because comment-hide allows preserving comments using `>>>`. Only block-style `/* */` comments support this feature. 67 | 68 | These comments are stored in the `.annotations/` folder at the root directory. You can locate the JSON file by following the current file name. 69 | 70 | ```json 71 | {"comments":[{"text":"\/\/ This is a comment"},{"text":"\/\/ Another comment"},{"multi":true,"text":"\/* This is a multi-line\n\/* This is a multi-line\n comment *\/"}],"originalContent":"\/* >>>\n This will not be hidden and will be visible to everyone\n*\/\n\nconst x = 42; \/\/ This is a comment\n\/* This is a multi-line\n comment *\/\n\/\/ Another comment","filePath":"Code\/project\/iusx\/test\/hhha.js"} 72 | ``` 73 | 74 | To restore comments, run `:CommentHideRestore`, and the plugin will reinsert comments based on line numbers and positions: 75 | 76 | ```js 77 | 0 /* >>> 78 | 1 This will not be hidden and will be 2 visible to everyone 79 | 2 */ 80 | 3 81 | 4 const x = 42; // This is a comment 82 | 5 /* This is a multi-line 83 | 6 comment */ 84 | 7 // Another comment 85 | ``` 86 | 87 | #### Next? 88 | 89 | - [ ] : Restore all comments 90 | - [ ] : Hide all file comments to the `.annotations/` directory 91 | - [x] : Fix space placeholders after `:CommentHideSave`. 92 | - [x] : Fix the absolute positioning issue. 93 | - [x] : Customize hiding and showing, for example, comment blocks containing `>>>` will not be hidden 94 | 95 | #### Support language / framework 96 | 97 | You can click look [utils.lua](https://github.com/my-rubbish/nvim.comment-hide/blob/main/lua/comment-hide/utils.lua#L20) file, Lnow supported languages: 98 | 99 | ``` 100 | local comment_patterns = { 101 | c = { single_patterns.slash, multi_patterns.c }, 102 | cpp = { single_patterns.slash, multi_patterns.c }, 103 | cs = { single_patterns.slash, multi_patterns.c }, 104 | css = { single_patterns.slash, multi_patterns.c }, 105 | go = { single_patterns.slash, multi_patterns.c }, 106 | java = { single_patterns.slash, multi_patterns.c }, 107 | javascript = { single_patterns.slash, multi_patterns.c }, 108 | javascriptreact = { single_patterns.slash, multi_patterns.c }, 109 | typescript = { single_patterns.slash, multi_patterns.c }, 110 | typescriptreact = { single_patterns.slash, multi_patterns.c }, 111 | scala = { single_patterns.slash, multi_patterns.c, multi_patterns.scala }, 112 | lua = { single_patterns.dash, multi_patterns.lua }, 113 | python = { 114 | single_patterns.hash, 115 | multi_patterns.python3, 116 | multi_patterns.python1, 117 | single_patterns.slash, 118 | multi_patterns.c, 119 | }, 120 | ruby = { single_patterns.hash, multi_patterns.ruby }, 121 | r = { single_patterns.hash }, 122 | rust = { single_patterns.slash, multi_patterns.c }, 123 | sh = { single_patterns.hash }, 124 | html = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 125 | markdown = { multi_patterns.html }, 126 | php = { single_patterns.slash, single_patterns.hash, multi_patterns.c }, 127 | scss = { single_patterns.slash, multi_patterns.c }, 128 | vue = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 129 | svelte = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 130 | elixir = { single_patterns.hash }, 131 | erlang = { single_patterns.percent }, 132 | ["html.handlebars"] = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 133 | nix = { single_patterns.hash }, 134 | yaml = { single_patterns.hash }, 135 | clojure = { single_patterns.semicolon }, 136 | bitbake = { single_patterns.semicolon }, 137 | cljc = { single_patterns.semicolon }, 138 | …… maybe more? 139 | } 140 | ``` 141 | 142 | For comment support, please refer to [comment_patterns](https://github.com/jiangxue-analysis/nvim.comment-hide/blob/main/lua/comment-hide/utils.lua), as each language has many different comment styles, so not all of them may be supported. 143 | 144 | ``` 145 | local single_patterns = { 146 | ["slash"] = { single = "//" }, 147 | ["hash"] = { single = "#" }, 148 | ["dash"] = { single = "--" }, 149 | ["percent"] = { single = "%" }, 150 | semicolon = { single = ";" } 151 | } 152 | 153 | local multi_patterns = { 154 | ["c"] = { multi_start = "/*", multi_end = "*/" }, 155 | ["lua"] = { multi_start = "--[[", multi_end = "]]" }, 156 | ["html"] = { multi_start = "" }, 157 | ["python3"] = { multi_start = '"""', multi_end = '"""' }, 158 | ["python1"] = { multi_start = "'''", multi_end = "'''" }, 159 | ["ruby"] = { multi_start = "=begin", multi_end = "=end" }, 160 | ["scala"] = { multi_start = "/**", multi_end = "*/" }, 161 | } 162 | ``` 163 | 164 | #### Example 165 | 166 | Welcome to use [meld](https://meldmerge.org/) for comparison 167 | 168 | ```js 169 | [RUST] 170 | 1 // COMMENT | 1 fn main() { 171 | 2 fn main() { | 2 172 | 3 /* | 3 println!("// Hello, World!"); 173 | 4 * COMMENT | 4 } 174 | 5 */ | 5 175 | 6 println!("// Hello, World!"); | 6 fn main() { 176 | 7 } | 7 177 | 8 // COMMENT | 8 println!("Hello, World! /* test */"); 178 | 9 | 9 } 179 | 10 fn main() { 180 | 11 /* 181 | 12 * COMMENT 182 | 13 */ 183 | 14 println!("Hello, World! /* test */"); // TEST 184 | 15 } 185 | 186 | 187 | 188 | [SCSS] 189 | 1 /* Set default margin and font for the body */ 190 | 2 body { | 1 body { 191 | 3 margin: 0; | 2 margin: 0; 192 | 4 font-family: Arial, sans-serif; | 3 font-family: Arial, sans-serif; 193 | 5 background-color: #f5f5f5; | 4 background-color: #f5f5f5; 194 | 6 h1 { | 5 h1 { 195 | 7 color: #333; // TEST | 6 color: #333; 196 | 8 text-align: center; | 7 text-align: center; 197 | 9 margin-top: 40px; | 8 margin-top: 40px; 198 | 10 } | 9 } 199 | 11 } | 10 } 200 | 201 | 202 | 203 | [TS] 204 | 1 // This is a single-line comment 205 | 2 const commentRegex = /\/\/.*|\/\*[\s\S]*?\*\/||#.*$/gm; 206 | 3 207 | 4 /* Multi-line | 1 const commentRegex = /\/\/.*|\/\*[\s\S]*?\*\/||#.*$/gm; 208 | 5 comment */ | 2 209 | 6 | 3 const regex = /\/\*[\s\S]*?\*\/|\/\/.*$/gm; 210 | 7 211 | 8 // String with // insideconst str = "This is a // string"; 212 | 9 const regex = /\/\*[\s\S]*?\*\/|\/\/.*$/gm; // Regex with comment-like content 213 | 214 | [TSX] 215 | 1 { | 1 { 216 | 2 // image | 2 217 | 3 } | 3 } 218 | 4 { 219 | 5 /* {isValidImageIcon 220 | 6 ? answer icon 221 | 7 : (icon && icon !== '') ? : 222 | 8 } */ 223 | 9 } 224 | ``` 225 | -------------------------------------------------------------------------------- /lua/comment-hide/utils.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local single_patterns = { 4 | ["slash"] = { single = "//" }, 5 | ["hash"] = { single = "#" }, 6 | ["dash"] = { single = "--" }, 7 | ["percent"] = { single = "%" }, 8 | semicolon = { single = ";" } 9 | } 10 | 11 | local multi_patterns = { 12 | ["c"] = { multi_start = "/*", multi_end = "*/" }, 13 | ["lua"] = { multi_start = "--[[", multi_end = "]]" }, 14 | ["html"] = { multi_start = "" }, 15 | ["python3"] = { multi_start = '"""', multi_end = '"""' }, 16 | ["python1"] = { multi_start = "'''", multi_end = "'''" }, 17 | ["ruby"] = { multi_start = "=begin", multi_end = "=end" }, 18 | ["scala"] = { multi_start = "/**", multi_end = "*/" }, 19 | } 20 | 21 | local comment_patterns = { 22 | c = { single_patterns.slash, multi_patterns.c }, 23 | cpp = { single_patterns.slash, multi_patterns.c }, 24 | cs = { single_patterns.slash, multi_patterns.c }, 25 | css = { single_patterns.slash, multi_patterns.c }, 26 | go = { single_patterns.slash, multi_patterns.c }, 27 | java = { single_patterns.slash, multi_patterns.c }, 28 | javascript = { single_patterns.slash, multi_patterns.c }, 29 | javascriptreact = { single_patterns.slash, multi_patterns.c }, 30 | typescript = { single_patterns.slash, multi_patterns.c }, 31 | typescriptreact = { single_patterns.slash, multi_patterns.c }, 32 | scala = { single_patterns.slash, multi_patterns.c, multi_patterns.scala }, 33 | lua = { single_patterns.dash, multi_patterns.lua }, 34 | python = { 35 | single_patterns.hash, 36 | multi_patterns.python3, 37 | multi_patterns.python1, 38 | single_patterns.slash, 39 | multi_patterns.c, 40 | }, 41 | ruby = { single_patterns.hash, multi_patterns.ruby }, 42 | r = { single_patterns.hash }, 43 | nim = { single_patterns.hash }, 44 | zsh = { single_patterns.hash }, 45 | rust = { single_patterns.slash, multi_patterns.c }, 46 | sh = { single_patterns.hash }, 47 | html = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 48 | markdown = { multi_patterns.html }, 49 | php = { single_patterns.slash, single_patterns.hash, multi_patterns.c }, 50 | scss = { single_patterns.slash, multi_patterns.c }, 51 | vue = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 52 | svelte = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 53 | elixir = { single_patterns.hash }, 54 | erlang = { single_patterns.percent }, 55 | ["html.handlebars"] = { multi_patterns.html, single_patterns.slash, multi_patterns.c }, 56 | nix = { single_patterns.hash }, 57 | yaml = { single_patterns.hash }, 58 | clojure = { single_patterns.semicolon }, 59 | bitbake = { single_patterns.semicolon }, 60 | cljc = { single_patterns.semicolon }, 61 | haskell = { single_patterns.dash }, 62 | } 63 | 64 | local function extract_heredocs(content, filetype) 65 | if filetype ~= "ruby" then 66 | return {}, content 67 | end 68 | 69 | local heredocs = {} 70 | local processed = content 71 | local i = 1 72 | 73 | processed = processed:gsub( 74 | "(<<[-~]?%s*['\"]?([%w_]+)['\"]?[\r\n])(.-)(\n%s*%2)", 75 | function(start, delim, content, ending) 76 | heredocs[i] = { 77 | delim = delim, 78 | content = start .. content .. ending, 79 | placeholder = "HEREDOC_" .. i .. "_", 80 | } 81 | i = i + 1 82 | return heredocs[i - 1].placeholder 83 | end 84 | ) 85 | 86 | return heredocs, processed 87 | end 88 | 89 | local function restore_heredocs(content, heredocs) 90 | for _, h in ipairs(heredocs) do 91 | content = content:gsub(h.placeholder, h.content) 92 | end 93 | return content 94 | end 95 | 96 | local function is_in_string_or_special(line, pos, filetype, heredocs) 97 | --- shebang 98 | if (filetype == "bash" or filetype == "sh" or filetype == "zsh") 99 | and pos == 1 and line:sub(1, 2) == "#!" then 100 | return true 101 | end 102 | 103 | local in_sq = false -- ' 104 | local in_dq = false -- " 105 | local in_bt = false -- ` 106 | local in_param = false -- ${} 107 | local param_depth = 0 108 | local in_cmd = false -- $() 109 | local cmd_depth = 0 110 | local in_arith = false -- $(()) 111 | local arith_depth = 0 112 | 113 | for i = 1, pos do 114 | local c = line:sub(i, i) 115 | local p = i > 1 and line:sub(i - 1, i - 1) or "" 116 | local n = line:sub(i + 1, i + 1) 117 | local nn = line:sub(i + 2, i + 2) 118 | 119 | -- backticks 120 | if not in_sq and not in_dq and c == "`" and p ~= "\\" then 121 | in_bt = not in_bt 122 | end 123 | 124 | if not in_bt then 125 | -- single quote 126 | if c == "'" and not in_dq and p ~= "\\" then 127 | in_sq = not in_sq 128 | 129 | -- double quote 130 | elseif c == '"' and not in_sq and p ~= "\\" then 131 | in_dq = not in_dq 132 | end 133 | end 134 | 135 | if not in_sq and not in_dq and not in_bt then 136 | -- ${ ... } 137 | if not in_param and c == "$" and n == "{" then 138 | in_param = true 139 | param_depth = 1 140 | elseif in_param then 141 | if c == "{" then 142 | param_depth = param_depth + 1 143 | elseif c == "}" then 144 | param_depth = param_depth - 1 145 | if param_depth == 0 then 146 | in_param = false 147 | end 148 | end 149 | end 150 | 151 | -- $(()) 152 | if not in_arith and c == "$" and n == "(" and nn == "(" then 153 | in_arith = true 154 | arith_depth = 1 155 | elseif in_arith then 156 | if c == "(" then 157 | arith_depth = arith_depth + 1 158 | elseif c == ")" then 159 | arith_depth = arith_depth - 1 160 | if arith_depth == 0 then 161 | in_arith = false 162 | end 163 | end 164 | end 165 | 166 | -- $() 167 | if not in_cmd and not in_arith and c == "$" and n == "(" then 168 | in_cmd = true 169 | cmd_depth = 1 170 | elseif in_cmd then 171 | if c == "(" then 172 | cmd_depth = cmd_depth + 1 173 | elseif c == ")" then 174 | cmd_depth = cmd_depth - 1 175 | if cmd_depth == 0 then 176 | in_cmd = false 177 | end 178 | end 179 | end 180 | end 181 | end 182 | 183 | return 184 | in_sq 185 | or in_dq 186 | or in_bt 187 | or in_param 188 | or in_cmd 189 | or in_arith 190 | end 191 | 192 | function M.extract_comments(content, filetype) 193 | local comments = {} 194 | local uncommented = content 195 | 196 | local heredocs, processed_content = extract_heredocs(content, filetype) 197 | uncommented = processed_content 198 | 199 | local protected = {} 200 | uncommented = uncommented:gsub("/%*%s*>>>.-[^%*/]-*/", function(match) 201 | table.insert(protected, match) 202 | return "PROTECTED_" .. #protected .. "_" 203 | end) 204 | 205 | local patterns = comment_patterns[filetype] or {} 206 | for _, pattern in ipairs(patterns) do 207 | if pattern.multi_start and pattern.multi_end then 208 | if filetype == "python" and (pattern.multi_start == '"""' or pattern.multi_start == "'''") then 209 | local content_lines = vim.split(uncommented, "\n") 210 | local new_lines = {} 211 | local in_comment = false 212 | local comment_start_line = 1 213 | local comment_content = {} 214 | 215 | for i, line in ipairs(content_lines) do 216 | if not in_comment then 217 | local trimmed = line:match("^%s*(.-)%s*$") 218 | if trimmed == pattern.multi_start then 219 | in_comment = true 220 | comment_start_line = i 221 | table.insert(comment_content, line) 222 | else 223 | table.insert(new_lines, line) 224 | end 225 | else 226 | table.insert(comment_content, line) 227 | local trimmed = line:match("^%s*(.-)%s*$") 228 | if trimmed == pattern.multi_end then 229 | in_comment = false 230 | table.insert(comments, { 231 | text = table.concat(comment_content, "\n"), 232 | multi = true, 233 | }) 234 | comment_content = {} 235 | end 236 | end 237 | end 238 | 239 | if in_comment then 240 | for _, l in ipairs(comment_content) do 241 | table.insert(new_lines, l) 242 | end 243 | end 244 | 245 | uncommented = table.concat(new_lines, "\n") 246 | else 247 | local lines = vim.split(uncommented, "\n") 248 | local new_lines = {} 249 | local inside_multi = false 250 | local comment_buf = {} 251 | local start_pat = pattern.multi_start 252 | local end_pat = pattern.multi_end 253 | local current_line_idx = 1 254 | 255 | while current_line_idx <= #lines do 256 | local line = lines[current_line_idx] 257 | local i = 1 258 | local output_line = "" 259 | 260 | while i <= #line do 261 | if not inside_multi then 262 | local s, e = line:find(vim.pesc(start_pat), i) 263 | if s then 264 | if is_in_string_or_special(line, s, filetype, {}) then 265 | output_line = output_line .. line:sub(i, e) 266 | i = e + 1 267 | else 268 | inside_multi = true 269 | comment_buf = { line:sub(s) } 270 | output_line = output_line .. line:sub(i, s - 1) 271 | i = e + 1 272 | end 273 | else 274 | output_line = output_line .. line:sub(i) 275 | break 276 | end 277 | else 278 | table.insert(comment_buf, line) 279 | local s, e = line:find(vim.pesc(end_pat), i) 280 | if s then 281 | inside_multi = false 282 | local comment = table.concat(comment_buf, "\n") 283 | table.insert(comments, { text = comment, multi = true }) 284 | i = e + 1 285 | comment_buf = {} 286 | else 287 | break 288 | end 289 | end 290 | end 291 | 292 | if not inside_multi then 293 | table.insert(new_lines, output_line) 294 | end 295 | 296 | current_line_idx = current_line_idx + 1 297 | end 298 | 299 | uncommented = table.concat(new_lines, "\n") 300 | end 301 | end 302 | 303 | if pattern.single then 304 | local lines = vim.split(uncommented, "\n") 305 | local new_lines = {} 306 | 307 | for line_num, line in ipairs(lines) do 308 | local new_line = "" 309 | local comment_start = nil 310 | 311 | for i = 1, #line do 312 | if line:sub(i, i + #pattern.single - 1) == pattern.single then 313 | if not is_in_string_or_special(line, i, filetype, heredocs) then 314 | comment_start = i 315 | break 316 | end 317 | end 318 | end 319 | 320 | if comment_start then 321 | table.insert(comments, { text = line:sub(comment_start) }) 322 | new_line = line:sub(1, comment_start - 1) 323 | else 324 | new_line = line 325 | end 326 | 327 | table.insert(new_lines, new_line) 328 | end 329 | 330 | uncommented = table.concat(new_lines, "\n") 331 | end 332 | end 333 | 334 | for i, match in ipairs(protected) do 335 | uncommented = uncommented:gsub("PROTECTED_" .. i .. "_", match) 336 | end 337 | 338 | uncommented = restore_heredocs(uncommented, heredocs) 339 | 340 | local lines = vim.split(uncommented, "\n") 341 | local cleaned_lines = {} 342 | local last_was_empty = false 343 | 344 | for _, line in ipairs(lines) do 345 | local trimmed = line:match("^%s*(.-)%s*$") 346 | if trimmed ~= "" then 347 | table.insert(cleaned_lines, line) 348 | last_was_empty = false 349 | elseif not last_was_empty then 350 | table.insert(cleaned_lines, "") 351 | last_was_empty = true 352 | end 353 | end 354 | 355 | while #cleaned_lines > 0 and cleaned_lines[1]:match("^%s*$") do 356 | table.remove(cleaned_lines, 1) 357 | end 358 | while #cleaned_lines > 0 and cleaned_lines[#cleaned_lines]:match("^%s*$") do 359 | table.remove(cleaned_lines) 360 | end 361 | 362 | return comments, table.concat(cleaned_lines, "\n") 363 | end 364 | 365 | return M 366 | -------------------------------------------------------------------------------- /test/yaml.yaml: -------------------------------------------------------------------------------- 1 | # Squirrel settings 2 | # encoding: utf-8 3 | # squirrel[.custom].yaml 是鼠须管的前端配置文件,小狼毫是 weasel[.custom].yaml 4 | # 各平台皮肤配置并不一致。 5 | # 6 | # 鼠须管内置皮肤展示: https://github.com/NavisLab/rime-pifu 7 | # 鼠须管界面配置指南: https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/鼠鬚管介面配置指南.md 8 | # 鼠须管作者写的图形化的皮肤设计器: https://github.com/LEOYoon-Tsaw/Squirrel-Designer 9 | 10 | # 要比共享目录的同名文件的 config_version 大才可以生效 11 | config_version: "2024-11-04" # config_version: '1.0' 12 | 13 | # options: last | default | _custom_ 14 | # last: the last used latin keyboard layout 15 | # default: US (ABC) keyboard layout 16 | # _custom_: keyboard layout of your choice, e.g. 'com.apple.keylayout.USExtended' or simply 'USExtended' 17 | keyboard_layout: last 18 | # for veteran chord-typist 19 | chord_duration: 0.1 # seconds 20 | # options: always | never | appropriate 21 | show_notifications_when: appropriate 22 | 23 | # ascii_mode、inline、no_inline、vim_mode 等等设定 24 | # 可参考 /Library/Input Methods/Squirrel.app/Contents/SharedSupport/squirrel.yaml 25 | app_options: 26 | # com.apple.Spotlight: 27 | # ascii_mode: true # 开启默认英文 28 | # com.microsoft.VSCode: 29 | # ascii_mode: false # 关闭默认英文 30 | 31 | style: 32 | # 选择皮肤,亮色与暗色主题 33 | color_scheme: heihei 34 | color_scheme_dark: heihei 35 | 36 | # 以下是预设选项。如果皮肤没写,则使用这些属性;如果皮肤写了,使用皮肤的。 37 | 38 | # 候选框与文字的排列方式 39 | candidate_list_layout: stacked # stacked | linear 皮肤横竖排显示是调整这个 40 | text_orientation: horizontal # horizontal | vertical 文字方向 41 | # 拼音或词句是否显示在键入位置 42 | inline_preedit: true # true | false 键入码显示位置 43 | inline_candidate: false # true | false 选中词显示位置 44 | 45 | # Whether candidate panel stick to screen edge to reduce jumping 46 | memorize_size: true 47 | # Whether transparent colors stack on each other 48 | mutual_exclusive: false 49 | # Whether to use a translucent background. Only visible when background color is transparent 50 | translucency: false 51 | 52 | corner_radius: 7 53 | hilited_corner_radius: 0 54 | border_height: -2 55 | border_width: -2 56 | # Space between candidates in stacked layout 57 | line_spacing: 5 58 | # Space between preedit and candidates in non-inline mode 59 | spacing: 8 60 | # A number greater than 0 renders shadow around high-lighted candidate 61 | shadow_size: 0 62 | # Controls non-hililighted candidate background size, relative to highlighted 63 | # Nagetive means shrink, positive meas expand 64 | #surrounding_extra_expansion: 0 65 | 66 | # format using %@ and %c is deprecated since 1.0, Squirrel 1.0 67 | # %@ is automatically expanded to "[candidate] [comment]" 68 | # and %c is replaced by "[label]" 69 | candidate_format: "[label]. [candidate] [comment]" # 候选项格式化 70 | 71 | # adjust the base line of text 72 | #base_offset: 0 73 | font_face: "Avenir" 74 | font_point: 16 75 | #label_font_face: 'Avenir' 76 | #label_font_point: 12 77 | #comment_font_face: 'Avenir' 78 | #comment_font_point: 16 79 | 80 | # 皮肤列表 81 | preset_color_schemes: 82 | # 对 purity_of_form 略微调整颜色,让色彩更柔和点,补全其他选项和注释 83 | purity_of_form_custom: 84 | name: "純粹的形式/Purity of Form Custom" 85 | author: 雨過之後、佛振 86 | # 如果将字体设置为 PingFangSC-Regular 87 | # 会让 🈶🈚️🉑🈲🉐 这几个 Emoji 失去彩色效果,留空反而可以显示。。。 88 | font_face: "" # 字体及大小 89 | font_point: 18 90 | label_font_face: "Helvetica" # 序号字体及大小 91 | label_font_point: 12 92 | comment_font_face: "Helvetica" # 注字体及大小 93 | comment_font_point: 16 94 | # candidate_list_layout: stacked # 候选项排列方向 stacked(默认) | linear 95 | # text_orientation: horizontal # 文字排列方向 horizontal(默认) | vertical 96 | inline_preedit: true # 键入码(拼音)是否显示在键入位置 true | false 97 | inline_candidate: false # 候选项(词句)是否显示在键入位置 true | false 98 | translucency: false # 磨砂: true | false 99 | mutual_exclusive: false # 色不叠加: true | false 100 | border_height: 0 # 外边框 高 101 | border_width: 0 # 外边框 宽 102 | corner_radius: 10 # 外边框 圆角半径 103 | hilited_corner_radius: 0 # 选中框 圆角半径 104 | surrounding_extra_expansion: 0 # 候选项背景相对大小? 105 | shadow_size: 0 # 阴影大小 106 | line_spacing: 5 # 行间距 107 | base_offset: 0 # 字基高 108 | alpha: 1 # 透明度,0~1 109 | spacing: 10 # 拼音与候选项之间的距离 (inline_preedit: false) 110 | color_space: srgb # 色彩空间: srgb | display_p3 111 | back_color: 0x545554 # 底色 112 | hilited_candidate_back_color: 0xE3E3E3 # 选中底色 113 | label_color: 0xBBBBBB # 序号颜色 114 | hilited_candidate_label_color: 0x4C4C4C # 选中序号颜色 115 | candidate_text_color: 0xEEEEEE # 文字颜色 116 | hilited_candidate_text_color: 0x000000 # 选中文字颜色 117 | comment_text_color: 0x808080 # 注颜色 118 | hilited_comment_text_color: 0x808080 # 选中注颜色 119 | text_color: 0x808080 # 拼音颜色 (inline_preedit: false) 120 | hilited_text_color: 0xEEEEEE # 选中拼音颜色 (inline_preedit: false) 121 | # candidate_back_color: # 候选项底色 122 | # preedit_back_color: # 拼音底色 (inline_preedit: false) 123 | # hilited_back_color: # 选中拼音底色 (inline_preedit: false) 124 | # border_color: # 外边框颜色 125 | 126 | # 下面是内置的皮肤 https://github.com/rime/squirrel/blob/master/data/squirrel.yaml 127 | 128 | heihei: 129 | name: "customized_color_scheme" 130 | font_face: "Helvetica" 131 | font_point: 20.0 132 | candidate_list_layout: linear 133 | text_orientation: horizontal 134 | inline_preedit: true 135 | translucency: true 136 | mutual_exclusive: true 137 | corner_radius: 5.0 138 | hilited_corner_radius: 5.0 139 | border_height: 5.0 140 | border_width: 5.0 141 | line_spacing: 10.0 142 | shadow_size: 20.0 143 | color_space: display_p3 144 | back_color: 0x67000000 145 | candidate_text_color: 0xD8FFFFFF 146 | comment_text_color: 0x3FFFFFFF 147 | label_color: 0xBBBBBB 148 | hilited_candidate_back_color: 0xFF733E 149 | hilited_candidate_text_color: 0xD8FFFFFF 150 | hilited_comment_text_color: 0xFFFFFF 151 | hilited_candidate_label_color: 0xD7CBC1 152 | text_color: 0x3FFFFFFF 153 | hilited_text_color: 0xD8FFFFFF 154 | 155 | native: 156 | name: 系統配色 157 | 158 | aqua: 159 | name: 碧水/Aqua 160 | author: 佛振 161 | text_color: 0x606060 162 | back_color: 0xeeeceeee 163 | candidate_text_color: 0x000000 164 | hilited_text_color: 0x000000 165 | hilited_candidate_text_color: 0xffffff 166 | hilited_candidate_back_color: 0xeefa3a0a 167 | comment_text_color: 0x5a5a5a 168 | hilited_comment_text_color: 0xfcac9d 169 | 170 | azure: 171 | name: 青天/Azure 172 | author: 佛振 173 | text_color: 0xcfa677 174 | candidate_text_color: 0xffeacc 175 | back_color: 0xee8b4e01 176 | hilited_text_color: 0xffeacc 177 | hilited_candidate_text_color: 0x7ffeff 178 | hilited_candidate_back_color: 0x00000000 179 | comment_text_color: 0xc69664 180 | 181 | luna: 182 | name: 明月/Luna 183 | author: 佛振 184 | text_color: 0xa5a5a5 185 | back_color: 0xdd000000 186 | candidate_text_color: 0xeceeee 187 | hilited_text_color: 0x7fffff 188 | hilited_candidate_text_color: 0x7fffff 189 | hilited_candidate_back_color: 0x40000000 190 | comment_text_color: 0xa5a5a5 191 | hilited_comment_text_color: 0x449c9d 192 | 193 | ink: 194 | name: 墨池/Ink 195 | author: 佛振 196 | text_color: 0x5a5a5a 197 | back_color: 0xeeffffff 198 | candidate_text_color: 0x000000 199 | hilited_text_color: 0x000000 200 | #hilited_back_color: 0xdddddd 201 | hilited_candidate_text_color: 0xffffff 202 | hilited_candidate_back_color: 0xcc000000 203 | comment_text_color: 0x5a5a5a 204 | hilited_comment_text_color: 0x808080 205 | 206 | lost_temple: 207 | name: 孤寺/Lost Temple 208 | author: 佛振 , based on ir_black 209 | text_color: 0xe8f3f6 210 | back_color: 0xee303030 211 | hilited_text_color: 0x82e6ca 212 | hilited_candidate_text_color: 0x000000 213 | hilited_candidate_back_color: 0x82e6ca 214 | comment_text_color: 0xbb82e6ca 215 | hilited_comment_text_color: 0xbb203d34 216 | 217 | dark_temple: 218 | name: 暗堂/Dark Temple 219 | author: 佛振 , based on ir_black 220 | text_color: 0x92f6da 221 | back_color: 0x222222 222 | candidate_text_color: 0xd8e3e6 223 | hilited_text_color: 0xffcf9a 224 | hilited_back_color: 0x222222 225 | hilited_candidate_text_color: 0x92f6da 226 | hilited_candidate_back_color: 0x10000000 # 0x333333 227 | comment_text_color: 0x606cff 228 | 229 | psionics: 230 | name: 幽能/Psionics 231 | author: 雨過之後、佛振 232 | text_color: 0xc2c2c2 233 | back_color: 0x444444 234 | candidate_text_color: 0xeeeeee 235 | hilited_text_color: 0xeeeeee 236 | hilited_back_color: 0x444444 237 | hilited_candidate_label_color: 0xfafafa 238 | hilited_candidate_text_color: 0xfafafa 239 | hilited_candidate_back_color: 0xd4bc00 240 | comment_text_color: 0x808080 241 | hilited_comment_text_color: 0x444444 242 | 243 | purity_of_form: 244 | name: 純粹的形式/Purity of Form 245 | author: 雨過之後、佛振 246 | text_color: 0xc2c2c2 247 | back_color: 0x444444 248 | candidate_text_color: 0xeeeeee 249 | hilited_text_color: 0xeeeeee 250 | hilited_back_color: 0x444444 251 | hilited_candidate_text_color: 0x000000 252 | hilited_candidate_back_color: 0xfafafa 253 | comment_text_color: 0x808080 254 | 255 | purity_of_essence: 256 | name: 純粹的本質/Purity of Essence 257 | author: 佛振 258 | text_color: 0x2c2ccc 259 | back_color: 0xfafafa 260 | candidate_text_color: 0x000000 261 | hilited_text_color: 0x000000 262 | hilited_back_color: 0xfafafa 263 | hilited_candidate_text_color: 0xeeeeee 264 | hilited_candidate_back_color: 0x444444 265 | comment_text_color: 0x808080 266 | 267 | starcraft: 268 | name: 星際我爭霸/StarCraft 269 | author: Contralisk , original artwork by Blizzard Entertainment 270 | text_color: 0xccaa88 271 | candidate_text_color: 0x30bb55 272 | back_color: 0xee000000 273 | border_color: 0x1010a0 274 | hilited_text_color: 0xfecb96 275 | hilited_back_color: 0x000000 276 | hilited_candidate_text_color: 0x70ffaf 277 | hilited_candidate_back_color: 0x00000000 278 | comment_text_color: 0x1010d0 279 | hilited_comment_text_color: 0x1010f0 280 | 281 | google: 282 | name: 谷歌/Google 283 | author: skoj 284 | text_color: 0x666666 #拼音串 285 | candidate_text_color: 0x000000 #非第一候选项 286 | back_color: 0xFFFFFF #背景 287 | border_color: 0xE2E2E2 #边框 288 | hilited_text_color: 0x000000 #拼音串高亮 289 | hilited_back_color: 0xFFFFFF #拼音串高亮背景 290 | hilited_candidate_text_color: 0xFFFFFF #第一候选项 291 | hilited_candidate_back_color: 0xCE7539 #第一候选项背景 292 | comment_text_color: 0x6D6D6D #注解文字 293 | hilited_comment_text_color: 0xEBC6B0 #注解文字高亮 294 | 295 | solarized_rock: 296 | name: 曬經石/Solarized Rock 297 | author: "Aben , based on Ethan Schoonover's Solarized color scheme" 298 | back_color: 0x362b00 299 | border_color: 0x362b00 300 | text_color: 0x8236d3 301 | hilited_text_color: 0x98a12a 302 | candidate_text_color: 0x969483 303 | comment_text_color: 0xc098a12a 304 | hilited_candidate_text_color: 0xffffff 305 | hilited_candidate_back_color: 0x8236d3 306 | hilited_comment_text_color: 0x362b00 307 | 308 | clean_white: 309 | name: 简约白/Clean White 310 | author: Chongyu Zhu , based on 搜狗「简约白」 311 | candidate_list_layout: linear 312 | candidate_format: "%c %@" 313 | corner_radius: 6 314 | border_height: 6 315 | border_width: 6 316 | font_point: 16 317 | label_font_point: 12 318 | label_color: 0x888888 319 | text_color: 0x808080 320 | hilited_text_color: 0x000000 321 | candidate_text_color: 0x000000 322 | comment_text_color: 0x808080 323 | back_color: 0xeeeeee 324 | hilited_candidate_label_color: 0xa0c98915 325 | hilited_candidate_text_color: 0xc98915 326 | hilited_candidate_back_color: 0xeeeeee 327 | 328 | apathy: 329 | name: 冷漠/Apathy 330 | author: LIANG Hai 331 | candidate_list_layout: linear # 水平排列 332 | inline_preedit: true #单行显示,false双行显示 333 | candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间 334 | corner_radius: 5 #候选条圆角 335 | border_height: 0 336 | border_width: 0 337 | back_color: 0xFFFFFF #候选条背景色 338 | font_face: "PingFangSC-Regular,HanaMinB" #候选词字体 339 | font_point: 16 #候选字词大小 340 | text_color: 0x424242 #高亮选中词颜色 341 | label_font_face: "STHeitiSC-Light" #候选词编号字体 342 | label_font_point: 12 #候选编号大小 343 | hilited_candidate_text_color: 0xEE6E00 #候选文字颜色 344 | hilited_candidate_back_color: 0xFFF0E4 #候选文字背景色 345 | comment_text_color: 0x999999 #拼音等提示文字颜色 346 | 347 | dust: 348 | name: 浮尘/Dust 349 | author: Superoutman 350 | candidate_list_layout: linear # 水平排列 351 | inline_preedit: true #单行显示,false双行显示 352 | candidate_format: "%c\u2005%@\u2005" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 353 | corner_radius: 2 #候选条圆角 354 | border_height: 3 # 窗口边界高度,大于圆角半径才生效 355 | border_width: 8 # 窗口边界宽度,大于圆角半径才生效 356 | back_color: 0xeeffffff #候选条背景色 357 | border_color: 0xE0B693 # 边框色 358 | font_face: "HYQiHei-55S Book,HanaMinA Regular" #候选词字体 359 | font_point: 14 #候选字词大小 360 | label_font_face: "SimHei" #候选词编号字体 361 | label_font_point: 10 #候选编号大小 362 | label_color: 0xcbcbcb # 预选栏编号颜色 363 | candidate_text_color: 0x555555 # 预选项文字颜色 364 | text_color: 0x424242 # 拼音行文字颜色,24位色值,16进制,BGR顺序 365 | comment_text_color: 0x999999 # 拼音等提示文字颜色 366 | hilited_text_color: 0x9e9e9e # 高亮拼音 (需要开启内嵌编码) 367 | hilited_candidate_text_color: 0x000000 # 第一候选项文字颜色 368 | hilited_candidate_back_color: 0xfff0e4 # 第一候选项背景背景色 369 | hilited_candidate_label_color: 0x555555 # 第一候选项编号颜色 370 | hilited_comment_text_color: 0x9e9e9e # 注解文字高亮 371 | 372 | mojave_dark: 373 | name: 沙漠夜/Mojave Dark 374 | author: xiehuc 375 | candidate_list_layout: linear # 水平排列 376 | inline_preedit: true # 单行显示,false双行显示 377 | candidate_format: "%c\u2005%@" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 378 | corner_radius: 5 # 候选条圆角 379 | hilited_corner_radius: 3 # 高亮圆角 380 | border_height: 6 # 窗口边界高度,大于圆角半径才生效 381 | border_width: 6 # 窗口边界宽度,大于圆角半径才生效 382 | font_face: "PingFangSC" # 候选词字体 383 | font_point: 16 # 候选字词大小 384 | label_font_point: 14 # 候选编号大小 385 | 386 | text_color: 0xdedddd # 拼音行文字颜色,24位色值,16进制,BGR顺序 387 | back_color: 0x252320 # 候选条背景色 388 | label_color: 0x888785 # 预选栏编号颜色 389 | border_color: 0x020202 # 边框色 390 | candidate_text_color: 0xdedddd # 预选项文字颜色 391 | hilited_text_color: 0xdedddd # 高亮拼音 (需要开启内嵌编码) 392 | hilited_back_color: 0x252320 # 高亮拼音 (需要开启内嵌编码) 393 | hilited_candidate_text_color: 0xffffff # 第一候选项文字颜色 394 | hilited_candidate_back_color: 0xcb5d00 # 第一候选项背景背景色 395 | hilited_candidate_label_color: 0xffffff # 第一候选项编号颜色 396 | comment_text_color: 0xdedddd # 拼音等提示文字颜色 397 | #hilited_comment_text_color: 0xdedddd # 注解文字高亮 398 | 399 | solarized_light: 400 | name: 曬經・日/Solarized Light 401 | author: 雪齋 402 | color_space: display_p3 # Only available on macOS 10.12+ 403 | back_color: 0xF0E5F6FB #Lab 97 , 0 , 10 404 | border_color: 0xEDFFFF #Lab 100, 0 , 10 405 | preedit_back_color: 0x403516 #Lab 20 ,-12,-12 406 | #candidate_back_color: 0x403516 #Lab 20 ,-12,-12 407 | candidate_text_color: 0x595E00 #Lab 35 ,-35,-5 408 | label_color: 0xA36407 #Lab 40 ,-10,-45 409 | comment_text_color: 0x005947 #Lab 35 ,-20, 65 410 | text_color: 0xA1A095 #Lab 65 ,-5 ,-2 411 | hilited_back_color: 0x4C4022 #Lab 25 ,-12,-12 412 | hilited_candidate_back_color: 0xD7E8ED #Lab 92 , 0 , 10 413 | hilited_candidate_text_color: 0x3942CB #Lab 50 , 65, 45 414 | hilited_candidate_label_color: 0x2566C6 #Lab 55 , 45, 65 415 | hilited_comment_text_color: 0x8144C2 #Lab 50 , 65,-5 416 | hilited_text_color: 0x2C8BAE #Lab 60 , 10, 65 417 | 418 | solarized_dark: 419 | name: 曬經・月/Solarized Dark 420 | author: 雪齋 421 | back_color: 0xF0352A0A #Lab 15 ,-12,-12 422 | border_color: 0x2A1F00 #Lab 10 ,-12,-12 423 | preedit_back_color: 0xD7E8ED #Lab 92 , 0 , 10 424 | #candidate_back_color: 0xD7E8ED #Lab 92 , 0 , 10 425 | candidate_text_color: 0x7389FF #Lab 75 , 65, 45 426 | label_color: 0x478DF4 #Lab 70 , 45, 65 427 | comment_text_color: 0xC38AFF #Lab 75 , 65,-5 428 | text_color: 0x756E5D #Lab 45 ,-7 ,-7 429 | hilited_back_color: 0xC9DADF #Lab 87 , 0 , 10 430 | hilited_candidate_back_color: 0x403516 #Lab 20 ,-12,-12 431 | hilited_candidate_text_color: 0x989F52 #Lab 60 ,-35,-5 432 | hilited_candidate_label_color: 0xCC8947 #Lab 55 ,-10,-45 433 | hilited_comment_text_color: 0x289989 #Lab 60 ,-20, 65 434 | hilited_text_color: 0xBE706D #Lab 50 , 15,-45 435 | -------------------------------------------------------------------------------- /test/erlang.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2009 2 | %% Nick Gerakines 3 | %% Jacob Vorreuter 4 | %% 5 | %% Permission is hereby granted, free of charge, to any person 6 | %% obtaining a copy of this software and associated documentation 7 | %% files (the "Software"), to deal in the Software without 8 | %% restriction, including without limitation the rights to use, 9 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | %% copies of the Software, and to permit persons to whom the 11 | %% Software is furnished to do so, subject to the following 12 | %% conditions: 13 | %% 14 | %% The above copyright notice and this permission notice shall be 15 | %% included in all copies or substantial portions of the Software. 16 | %% 17 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | %% OTHER DEALINGS IN THE SOFTWARE. 25 | %% 26 | %% @doc A protcol buffers encoding and decoding module. 27 | -module(protobuffs). 28 | 29 | %% Public 30 | -export([main/1, encode/3, encode_packed/3, decode/2, decode_packed/2]). 31 | 32 | %% Used by generated *_pb file. Not intended to used by User 33 | -export([next_field_num/1, skip_next_field/1]). 34 | -export([encode_varint/1, decode_varint/1]). 35 | 36 | %% Will be removed from export, only intended for internal usage 37 | -deprecated([{read_field_num_and_wire_type,1,next_version}]). 38 | -deprecated([{decode_value,3,next_version}]). 39 | -export([read_field_num_and_wire_type/1, decode_value/3]). 40 | 41 | -define(TYPE_VARINT, 0). 42 | -define(TYPE_64BIT, 1). 43 | -define(TYPE_STRING, 2). 44 | -define(TYPE_START_GROUP, 3). 45 | -define(TYPE_END_GROUP, 4). 46 | -define(TYPE_32BIT, 5). 47 | 48 | -type encoded_field_type() :: 49 | ?TYPE_VARINT | ?TYPE_64BIT | ?TYPE_STRING | 50 | ?TYPE_START_GROUP | ?TYPE_END_GROUP | ?TYPE_32BIT. 51 | 52 | -type field_type() :: bool | enum | int32 | uint32 | int64 | 53 | uint64 | sint32 | sint64 | fixed32 | 54 | sfixed32 | fixed64 | sfixed64 | string | 55 | bytes | float | double. 56 | 57 | %%-------------------------------------------------------------------- 58 | %% @doc Runs command line utility 59 | %% @end 60 | %%-------------------------------------------------------------------- 61 | 62 | main(Args) -> 63 | protobuffs_cli:main(Args). 64 | 65 | %%-------------------------------------------------------------------- 66 | %% @doc Encode an Erlang data structure into a Protocol Buffers value. 67 | %% @end 68 | %%-------------------------------------------------------------------- 69 | -spec encode(FieldID :: non_neg_integer(), 70 | Value :: any(), 71 | Type :: field_type()) -> 72 | iodata(). 73 | encode(FieldID, Value, Type) -> 74 | encode_internal(FieldID, Value, Type). 75 | 76 | %%-------------------------------------------------------------------- 77 | %% @doc Encode an list of Erlang data structure into a Protocol Buffers values. 78 | %% @end 79 | %%-------------------------------------------------------------------- 80 | -spec encode_packed(FieldID :: non_neg_integer(), 81 | Values :: list(), 82 | Type :: field_type()) -> 83 | binary(). 84 | encode_packed(_FieldID, [], _Type) -> 85 | <<>>; 86 | encode_packed(FieldID, Values, Type) -> 87 | PackedValues = encode_packed_internal(Values,Type,[]), 88 | Size = encode_varint(iolist_size(PackedValues)), 89 | [encode_field_tag(FieldID, ?TYPE_STRING),Size,PackedValues]. 90 | 91 | %% @hidden 92 | -spec encode_internal(FieldID :: non_neg_integer(), 93 | Value :: any(), 94 | Type :: field_type()) -> 95 | iolist(). 96 | encode_internal(FieldID, false, bool) -> 97 | encode_internal(FieldID, 0, int32); 98 | encode_internal(FieldID, true, bool) -> 99 | encode_internal(FieldID, 1, int32); 100 | encode_internal(FieldID, Integer, enum) when is_integer(Integer) -> 101 | encode_internal(FieldID, Integer, int32); 102 | encode_internal(FieldID, Integer, int32) when Integer >= -16#80000000, 103 | Integer < 0 -> 104 | encode_internal(FieldID, Integer, int64); 105 | encode_internal(FieldID, Integer, int64) when Integer >= -16#8000000000000000, 106 | Integer < 0 -> 107 | encode_internal(FieldID, Integer + (1 bsl 64), uint64); 108 | encode_internal(FieldID, Integer, int32) when is_integer(Integer), 109 | Integer >= -16#80000000, 110 | Integer =< 16#7fffffff -> 111 | encode_varint_field(FieldID, Integer); 112 | encode_internal(FieldID, Integer, uint32) when Integer band 16#ffffffff =:= Integer -> 113 | encode_varint_field(FieldID, Integer); 114 | encode_internal(FieldID, Integer, int64) when is_integer(Integer), 115 | Integer >= -16#8000000000000000, 116 | Integer =< 16#7fffffffffffffff -> 117 | encode_varint_field(FieldID, Integer); 118 | encode_internal(FieldID, Integer, uint64) when Integer band 16#ffffffffffffffff =:= Integer -> 119 | encode_varint_field(FieldID, Integer); 120 | encode_internal(FieldID, Integer, bool) when Integer =:= 1; Integer =:= 0 -> 121 | encode_varint_field(FieldID, Integer); 122 | encode_internal(FieldID, Integer, sint32) when is_integer(Integer), 123 | Integer >= -16#80000000, 124 | Integer < 0 -> 125 | encode_varint_field(FieldID, bnot (Integer bsl 1)); 126 | encode_internal(FieldID, Integer, sint64) when is_integer(Integer), 127 | Integer >= -16#8000000000000000, 128 | Integer < 0 -> 129 | encode_varint_field(FieldID, bnot (Integer bsl 1)); 130 | encode_internal(FieldID, Integer, sint32) when is_integer(Integer), 131 | Integer >= 0, 132 | Integer =< 16#7fffffff -> 133 | encode_varint_field(FieldID, Integer bsl 1); 134 | encode_internal(FieldID, Integer, sint64) when is_integer(Integer), 135 | Integer >= 0, 136 | Integer =< 16#7fffffffffffffff -> 137 | encode_varint_field(FieldID, Integer bsl 1); 138 | encode_internal(FieldID, Integer, fixed32) when Integer band 16#ffffffff =:= Integer -> 139 | [encode_field_tag(FieldID, ?TYPE_32BIT), <>]; 140 | encode_internal(FieldID, Integer, sfixed32) when Integer >= -16#80000000, 141 | Integer =< 16#7fffffff -> 142 | [encode_field_tag(FieldID, ?TYPE_32BIT), <>]; 143 | encode_internal(FieldID, Integer, fixed64) when Integer band 16#ffffffffffffffff =:= Integer -> 144 | [encode_field_tag(FieldID, ?TYPE_64BIT), <>]; 145 | encode_internal(FieldID, Integer, sfixed64) when Integer >= -16#8000000000000000, 146 | Integer =< 16#7fffffffffffffff -> 147 | [encode_field_tag(FieldID, ?TYPE_64BIT), <>]; 148 | encode_internal(FieldID, String, string) when is_list(String) -> 149 | encode_internal(FieldID, unicode:characters_to_binary(String), string); 150 | encode_internal(FieldID, String, string) when is_binary(String) -> 151 | encode_internal(FieldID, String, bytes); 152 | encode_internal(FieldID, Bytes, bytes) when is_binary(Bytes); is_list(Bytes) -> 153 | [encode_field_tag(FieldID, ?TYPE_STRING), encode_varint(iolist_size(Bytes)), Bytes]; 154 | encode_internal(FieldID, Float, float) when is_integer(Float) -> 155 | encode_internal(FieldID, Float + 0.0, float); 156 | encode_internal(FieldID, Float, float) when is_float(Float) -> 157 | [encode_field_tag(FieldID, ?TYPE_32BIT), <>]; 158 | encode_internal(FieldID, nan, float) -> 159 | [encode_field_tag(FieldID, ?TYPE_32BIT), <<0:16,192:8,255:8>>]; 160 | encode_internal(FieldID, infinity, float) -> 161 | [encode_field_tag(FieldID, ?TYPE_32BIT), <<0:16,128:8,127:8>>]; 162 | encode_internal(FieldID, '-infinity', float) -> 163 | [encode_field_tag(FieldID, ?TYPE_32BIT), <<0:16,128:8,255:8>>]; 164 | encode_internal(FieldID, Float, double) when is_integer(Float) -> 165 | encode_internal(FieldID, Float + 0.0, double); 166 | encode_internal(FieldID, Float, double) when is_float(Float) -> 167 | [encode_field_tag(FieldID, ?TYPE_64BIT), <>]; 168 | encode_internal(FieldID, nan, double) -> 169 | [encode_field_tag(FieldID, ?TYPE_64BIT), <<0:48,16#F8,16#FF>>]; 170 | encode_internal(FieldID, infinity, double) -> 171 | [encode_field_tag(FieldID, ?TYPE_64BIT), <<0:48,16#F0,16#7F>>]; 172 | encode_internal(FieldID, '-infinity', double) -> 173 | [encode_field_tag(FieldID, ?TYPE_64BIT), <<0:48,16#F0,16#FF>>]; 174 | encode_internal(FieldID, Value, Type) -> 175 | erlang:error(badarg,[FieldID, Value, Type]). 176 | 177 | 178 | %% @hidden 179 | -spec encode_packed_internal(Values :: list(), 180 | ExpectedType :: field_type(), 181 | Acc :: list()) -> 182 | iolist(). 183 | encode_packed_internal([],_Type,Acc) -> 184 | lists:reverse(Acc); 185 | encode_packed_internal([Value|Tail], ExpectedType, Acc) -> 186 | [_|V] = encode_internal(1, Value, ExpectedType), 187 | encode_packed_internal(Tail, ExpectedType, [V|Acc]). 188 | 189 | %%-------------------------------------------------------------------- 190 | %% @doc Will be hidden in future releases 191 | %% @end 192 | %%-------------------------------------------------------------------- 193 | -spec read_field_num_and_wire_type(Bytes :: binary()) -> 194 | {{non_neg_integer(), encoded_field_type()}, binary()}. 195 | read_field_num_and_wire_type(<<_:8,_/binary>> = Bytes) -> 196 | {Tag, Rest} = decode_varint(Bytes), 197 | FieldID = Tag bsr 3, 198 | case Tag band 7 of 199 | ?TYPE_VARINT -> 200 | {{FieldID, ?TYPE_VARINT}, Rest}; 201 | ?TYPE_64BIT -> 202 | {{FieldID, ?TYPE_64BIT}, Rest}; 203 | ?TYPE_STRING -> 204 | {{FieldID, ?TYPE_STRING}, Rest}; 205 | ?TYPE_START_GROUP -> 206 | {{FieldID, ?TYPE_START_GROUP}, Rest}; 207 | ?TYPE_END_GROUP -> 208 | {{FieldID, ?TYPE_END_GROUP}, Rest}; 209 | ?TYPE_32BIT -> 210 | {{FieldID, ?TYPE_32BIT}, Rest}; 211 | _Else -> 212 | erlang:throw({error, "Error decoding type"}) 213 | end; 214 | read_field_num_and_wire_type(Bytes) -> 215 | erlang:error(badarg,[Bytes]). 216 | 217 | %%-------------------------------------------------------------------- 218 | %% @doc Decode a single value from a protobuffs data structure 219 | %% @end 220 | %%-------------------------------------------------------------------- 221 | -spec decode(Bytes :: binary(), ExpectedType :: field_type()) -> 222 | {{non_neg_integer(), any()}, binary()}. 223 | decode(Bytes, ExpectedType) -> 224 | {{FieldID, WireType}, Rest} = read_field_num_and_wire_type(Bytes), 225 | {Value, Rest1} = decode_value(Rest, WireType, ExpectedType), 226 | {{FieldID, Value}, Rest1}. 227 | 228 | %%-------------------------------------------------------------------- 229 | %% @doc Decode packed values from a protobuffs data structure 230 | %% @end 231 | %%-------------------------------------------------------------------- 232 | -spec decode_packed(Bytes :: binary(), ExpectedType :: field_type()) -> 233 | {{non_neg_integer(), any()}, binary()}. 234 | decode_packed(Bytes, ExpectedType) -> 235 | case read_field_num_and_wire_type(Bytes) of 236 | {{FieldID, ?TYPE_STRING}, Rest} -> 237 | {Length, Rest1} = decode_varint(Rest), 238 | {Packed,Rest2} = split_binary(Rest1, Length), 239 | Values = decode_packed_values(Packed, ExpectedType, []), 240 | {{FieldID, Values},Rest2}; 241 | _Else -> 242 | erlang:error(badarg) 243 | end. 244 | 245 | %%-------------------------------------------------------------------- 246 | %% @doc Returns the next field number id from a protobuffs data structure 247 | %% @end 248 | %%-------------------------------------------------------------------- 249 | -spec next_field_num(Bytes :: binary()) -> {ok,non_neg_integer()}. 250 | next_field_num(Bytes) -> 251 | {{FieldID,_WiredType}, _Rest} = read_field_num_and_wire_type(Bytes), 252 | {ok,FieldID}. 253 | 254 | %%-------------------------------------------------------------------- 255 | %% @doc Skips the field at the front of the message, effectively ignoring it. 256 | %% @end 257 | %%-------------------------------------------------------------------- 258 | -spec skip_next_field(Bytes :: binary()) -> 259 | {ok, binary()}. 260 | skip_next_field(Bytes) -> 261 | {{_FieldId, WireType}, Rest} = read_field_num_and_wire_type(Bytes), 262 | case WireType of 263 | ?TYPE_VARINT -> 264 | {_, Rest1} = decode_varint(Rest); 265 | ?TYPE_64BIT -> 266 | <<_:64, Rest1/binary>> = Rest; 267 | ?TYPE_32BIT -> 268 | <<_:32, Rest1/binary>> = Rest; 269 | ?TYPE_STRING -> 270 | {_, Rest1} = decode_value(Rest, WireType, string); 271 | _ -> 272 | Rest1 = Rest 273 | end, 274 | {ok, Rest1}. 275 | 276 | %% @hidden 277 | -spec decode_packed_values(Bytes :: binary(), 278 | Type :: field_type(), 279 | Acc :: list()) -> 280 | iolist(). 281 | decode_packed_values(<<>>, _, Acc) -> 282 | lists:reverse(Acc); 283 | decode_packed_values(Bytes, bool, Acc) -> 284 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, bool), 285 | decode_packed_values(Rest, bool, [Value|Acc]); 286 | decode_packed_values(Bytes, enum, Acc) -> 287 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, enum), 288 | decode_packed_values(Rest, enum, [Value|Acc]); 289 | decode_packed_values(Bytes, int32, Acc) -> 290 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, int32), 291 | decode_packed_values(Rest, int32, [Value|Acc]); 292 | decode_packed_values(Bytes, uint32, Acc) -> 293 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, uint32), 294 | decode_packed_values(Rest, uint32, [Value|Acc]); 295 | decode_packed_values(Bytes, sint32, Acc) -> 296 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, sint32), 297 | decode_packed_values(Rest, sint32, [Value|Acc]); 298 | decode_packed_values(Bytes, int64, Acc) -> 299 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, int64), 300 | decode_packed_values(Rest, int64, [Value|Acc]); 301 | decode_packed_values(Bytes, uint64, Acc) -> 302 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, uint64), 303 | decode_packed_values(Rest, uint64, [Value|Acc]); 304 | decode_packed_values(Bytes, sint64, Acc) -> 305 | {Value,Rest} = decode_value(Bytes,?TYPE_VARINT, sint64), 306 | decode_packed_values(Rest, sint64, [Value|Acc]); 307 | decode_packed_values(Bytes, float, Acc) -> 308 | {Value,Rest} = decode_value(Bytes,?TYPE_32BIT, float), 309 | decode_packed_values(Rest, float, [Value|Acc]); 310 | decode_packed_values(Bytes, double, Acc) -> 311 | {Value,Rest} = decode_value(Bytes,?TYPE_64BIT, double), 312 | decode_packed_values(Rest, double, [Value|Acc]); 313 | decode_packed_values(Bytes, Type, Acc) -> 314 | erlang:error(badarg,[Bytes,Type,Acc]). 315 | 316 | 317 | %%-------------------------------------------------------------------- 318 | %% @doc Will be hidden in future releases 319 | %% @end 320 | %%-------------------------------------------------------------------- 321 | -spec decode_value(Bytes :: binary(), 322 | WireType :: encoded_field_type(), 323 | ExpectedType :: field_type()) -> 324 | {any(),binary()}. 325 | decode_value(Bytes, ?TYPE_VARINT, ExpectedType) -> 326 | {Value, Rest} = decode_varint(Bytes), 327 | {typecast(Value, ExpectedType), Rest}; 328 | decode_value(Bytes, ?TYPE_STRING, string) -> 329 | {Length, Rest} = decode_varint(Bytes), 330 | {Value,Rest1} = split_binary(Rest, Length), 331 | {[C || <> <= Value],Rest1}; 332 | decode_value(Bytes, ?TYPE_STRING, bytes) -> 333 | {Length, Rest} = decode_varint(Bytes), 334 | split_binary(Rest, Length); 335 | decode_value(<>, ?TYPE_64BIT, fixed64) -> 336 | {Value, Rest}; 337 | decode_value(<>, ?TYPE_64BIT, fixed32) -> 338 | {Value, Rest}; 339 | decode_value(<>, ?TYPE_64BIT, sfixed64) -> 340 | {Value, Rest}; 341 | decode_value(<>, ?TYPE_64BIT, sfixed32) -> 342 | {Value, Rest}; 343 | decode_value(<>, ?TYPE_32BIT, Type) when Type =:= fixed32; Type =:= fixed64 -> 344 | {Value, Rest}; 345 | decode_value(<>, ?TYPE_32BIT, Type) when Type =:= sfixed32; Type =:= sfixed64 -> 346 | {Value, Rest}; 347 | decode_value(<>, ?TYPE_32BIT, float) -> 348 | {Value + 0.0, Rest}; 349 | decode_value(<<0:16, 128:8, 127:8, Rest/binary>>, ?TYPE_32BIT, float) -> 350 | {infinity, Rest}; 351 | decode_value(<<0:16, 128:8, 255:8, Rest/binary>>, ?TYPE_32BIT, float) -> 352 | {'-infinity', Rest}; 353 | decode_value(<<_:16, 2#1:1, _:7, _:1, 2#1111111:7, Rest/binary>>, ?TYPE_32BIT, float) -> 354 | {nan, Rest}; 355 | decode_value(<>, ?TYPE_64BIT, double) -> 356 | {Value + 0.0, Rest}; 357 | decode_value(<<0:48, 240:8, 127:8, Rest/binary>>, ?TYPE_64BIT, double) -> 358 | {infinity, Rest}; 359 | decode_value(<<0:48, 240:8, 255:8, Rest/binary>>, ?TYPE_64BIT, double) -> 360 | {'-infinity', Rest}; 361 | decode_value(<<_:48, 2#1111:4, _:4, _:1, 2#1111111:7, Rest/binary>>, ?TYPE_64BIT, double) -> 362 | {nan, Rest}; 363 | decode_value(Bytes,WireType,ExpectedType) -> 364 | erlang:error(badarg,[Bytes,WireType,ExpectedType]). 365 | 366 | %% @hidden 367 | -spec typecast(Value :: any(), Type :: field_type()) -> 368 | any(). 369 | typecast(Value, SignedType) when SignedType =:= int32; SignedType =:= int64; SignedType =:= enum -> 370 | if 371 | Value band 16#8000000000000000 =/= 0 -> Value - 16#10000000000000000; 372 | true -> Value 373 | end; 374 | typecast(Value, SignedType) when SignedType =:= sint32; SignedType =:= sint64 -> 375 | (Value bsr 1) bxor (-(Value band 1)); 376 | typecast(Value, Type) when Type =:= bool -> 377 | Value =:= 1; 378 | typecast(Value, _) -> 379 | Value. 380 | 381 | %% @hidden 382 | -spec encode_field_tag(FieldID :: non_neg_integer(), 383 | FieldType :: encoded_field_type()) -> 384 | iodata(). 385 | encode_field_tag(FieldID, FieldType) when FieldID band 16#3fffffff =:= FieldID -> 386 | encode_varint((FieldID bsl 3) bor FieldType). 387 | 388 | %% @hidden 389 | -spec encode_varint_field(FieldID :: non_neg_integer(), 390 | Integer :: integer()) -> 391 | iolist(). 392 | encode_varint_field(FieldID, Integer) -> 393 | [encode_field_tag(FieldID, ?TYPE_VARINT), encode_varint(Integer)]. 394 | 395 | %% @hidden 396 | -spec encode_varint(I :: integer()) -> 397 | iodata(). 398 | encode_varint(I) -> 399 | encode_varint(I, []). 400 | 401 | %% @hidden 402 | -spec encode_varint(I :: integer(), Acc :: list()) -> 403 | iodata(). 404 | encode_varint(I, Acc) when I =< 16#7f -> 405 | lists:reverse([I | Acc]); 406 | encode_varint(I, Acc) -> 407 | Last_Seven_Bits = (I - ((I bsr 7) bsl 7)), 408 | First_X_Bits = (I bsr 7), 409 | With_Leading_Bit = Last_Seven_Bits bor 16#80, 410 | encode_varint(First_X_Bits, [With_Leading_Bit|Acc]). 411 | 412 | %% @hidden 413 | -spec decode_varint(Bytes :: binary()) -> 414 | {integer(), binary()}. 415 | decode_varint(Bytes) -> 416 | decode_varint(Bytes, 0, 0). 417 | 418 | %% @hidden 419 | -spec decode_varint(Bytes :: binary(), non_neg_integer(), non_neg_integer()) -> 420 | {integer(), binary()}. 421 | decode_varint(<<0:1, I:7, Rest/binary>>, Int, Depth) -> 422 | {(I bsl Depth) bor Int, Rest}; 423 | decode_varint(<<1:1, I:7, Rest/binary>>, Int, Depth) -> 424 | decode_varint(Rest, (I bsl Depth) bor Int, Depth + 7); 425 | decode_varint(Bin, Int, Depth) -> 426 | erlang:error(badarg, [Bin, Int, Depth]). 427 | --------------------------------------------------------------------------------