├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── css └── ts.css ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── js ├── techscriptor.js └── ts.min.js ├── preview.jpg ├── site.webmanifest └── tests.md /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guidelines 2 | 3 | First of all, thanks for thinking of contributing to this project. 😄 4 | 5 | Before sending a Pull Request, please make sure that you're assigned the task on 6 | a GitHub issue. 7 | 8 | * If a relevant issue already exists, discuss on the issue and get it assigned 9 | to yourself on GitHub. 10 | * If no relevant issue exists, open a new issue and get it assigned to yourself 11 | on GitHub. 12 | 13 | Please proceed with a Pull Request only after you're assigned. It'd be sad if 14 | your Pull Request (and your hardwork) isn't accepted just because it isn't 15 | ideologically compatible. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Constantin Brîncoveanu 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # techscriptor [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Techscriptor%20makes%20your%20writing%20precise%20and%20clear&url=https://www.techscriptor.com&hashtags=developers,technical,writing,markdown,editor,documentation) 2 | 3 | ![Netlify](https://img.shields.io/netlify/9be3ff72-d866-4631-ba2e-36705e1aad58) 4 | ![License](https://img.shields.io/github/license/cbrincoveanu/techscriptor) 5 | ![Stars](https://img.shields.io/github/stars/cbrincoveanu/techscriptor) 6 | ![Forks](https://img.shields.io/github/forks/cbrincoveanu/techscriptor) 7 | 8 | Markdown editor for technical writing. 9 | 10 | [![Preview](/preview.jpg)](https://www.techscriptor.com) 11 | 12 | > ***Techscriptor*** is a Markdown editor that makes your writing precise and 13 | > clear. 14 | 15 | Techscriptor helps you avoid the following: 16 | 17 | * **Long sentences:** Make your sentences short and clear. 18 | * **Passive voice:** Use the active voice most of the time. 19 | * **Generic expressions:** Reduce imprecise, weak, or generic words. 20 | * **Adverbs:** Avoid adverbs if they add no significant meaning. 21 | 22 | A live demo is available here: https://www.techscriptor.com 23 | 24 | ## Getting Started 25 | 26 | You only need a web browser for viewing `index.html` after cloning the 27 | repository to your local machine. 28 | 29 | Techscriptor loads its dependencies from CDNs (see below). Other than that, it 30 | loads `ts.min.js` which is the minified[^1] version of `js/techscriptor.js`. 31 | 32 | [^1]: minification using https://www.toptal.com/developers/javascript-minifier 33 | 34 | ## Dependencies 35 | 36 | Techscriptor depends on the following (loaded from 37 | [jsdelivr.com](https://www.jsdelivr.com/) and 38 | [cdnjs.cloudflare.com](https://cdnjs.cloudflare.com/)): 39 | 40 | * [Ace](https://ace.c9.io/) v1.10.1 41 | * [markdown-it](https://github.com/markdown-it/markdown-it) v13.0.1 42 | * [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) v3.0.3 43 | * [highlight.js](https://highlightjs.org/) v11.6.0 44 | * [Bootstrap](https://getbootstrap.com/) v5.2.1 45 | 46 | ## Contribute 47 | 48 | Your contributions are always welcome! Please have a look at the [contribution 49 | guidelines](/CONTRIBUTING.md) first. 🎉 50 | 51 | ## License 52 | 53 | This software is licensed under the MIT license. 54 | -------------------------------------------------------------------------------- /android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/android-chrome-192x192.png -------------------------------------------------------------------------------- /android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/android-chrome-512x512.png -------------------------------------------------------------------------------- /apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/apple-touch-icon.png -------------------------------------------------------------------------------- /css/ts.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | #editor { 5 | position: absolute; 6 | top: 0; 7 | right: 0; 8 | bottom: 0; 9 | left: 0; 10 | } 11 | #mdviewContainer { 12 | background: #f1f1ff; 13 | } 14 | #mdview { 15 | padding: 20px; 16 | } 17 | blockquote { 18 | background: #f9f9f9; 19 | border-left: 3px solid #ccc; 20 | margin: 1.5em 10px; 21 | padding: 0.5em 10px; 22 | } 23 | blockquote p { 24 | display: inline-block; 25 | } 26 | -------------------------------------------------------------------------------- /favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/favicon-16x16.png -------------------------------------------------------------------------------- /favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/favicon-32x32.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | Techscriptor 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
# Built for Technical Writing 27 | 28 | > ![Techscriptor](/favicon-32x32.png) 29 | > ***Techscriptor*** is a Markdown editor that makes your writing precise and 30 | > clear. 31 | 32 | Techscriptor helps you avoid the following: 33 | 34 | * **Long sentences:** Make your sentences short and clear. If you see a yellow 35 | highlight, your sentence is so dense and complicated that your readers will get 36 | lost trying to understand it and then you should consider splitting it up. 37 | * **Passive voice:** Use the active voice most of the time. If the passive voice 38 | is used, sentences tend to be more wordy and vague. 39 | * **Generic expressions:** Reduce imprecise, weak, or generic words. There are 40 | all kinds of generic expressions. It can happen that your readers will find them 41 | difficult to interpret. Try replacing them with more specific expressions. 42 | * **Adverbs:** Avoid adverbs if they add no significant meaning. Unfortunately, 43 | adverbs sometimes make technical readers bark loudly and ferociously. 44 | 45 | Techscriptor renders Markdown in the browser: 46 | 47 | * inline `code` 48 | * code blocks with syntax highlighting for many languages: 49 | ```python 50 | def F(n): 51 | if n == 0 or n == 1: 52 | return n 53 | else: 54 | return F(n - 1) + F(n - 2) 55 | 56 | x = 7 57 | print(f"F({x}) = {F(x)}") 58 | ``` 59 | * footnotes[^1] 60 | 61 | [^1]: My reference. 62 | 63 | * tables: 64 | 65 | | Tables | Are | Cool | 66 | | ------------- |:-------------:| -----:| 67 | | col 3 is | right-aligned | $1600 | 68 | | col 2 is | centered | $12 | 69 | | zebra stripes | are neat | $1 | 70 | 71 | * blockquotes: 72 | > Life is a long lesson in humility. 73 | > — James M. Barrie 74 |
75 |
76 |
77 |

78 | Star 79 | Fork 80 |

81 |
82 |
Warnings: 0 · Sentences: 0 · Words: 0 · Characters: 0
83 |
84 |

85 | Techscriptor 86 | v0.1.2 by cbrincoveanu 87 |

88 |
89 |
90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /js/techscriptor.js: -------------------------------------------------------------------------------- 1 | var editor = ace.edit("editor"); 2 | editor.setTheme("ace/theme/chrome"); 3 | editor.session.setMode("ace/mode/markdown"); 4 | editor.setOptions({ 5 | fontSize: 14, 6 | vScrollBarAlwaysVisible: true, 7 | wrap: true 8 | }); 9 | function loadPopovers() { 10 | var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); 11 | // console.log(popoverTriggerList); 12 | var popoverList = popoverTriggerList.map(function (element) { 13 | return new bootstrap.Popover(element, { 14 | trigger: 'hover focus', 15 | html: true 16 | }); 17 | }); 18 | } 19 | function getPopover(text, message, color) { 20 | return ` 22 | ${text} 23 | `; 24 | } 25 | var md = window.markdownit().use(window.markdownitFootnote); 26 | const beforeUnloadListener = (event) => { 27 | event.preventDefault(); 28 | return event.returnValue = "Are you sure you want to exit?"; 29 | }; 30 | function getReplacementString(i) { 31 | return "[#r" + i + "]"; 32 | } 33 | function countWords(str) { 34 | const arr = str.split(' '); 35 | return arr.filter(word => word !== '').length; 36 | } 37 | function countSentences(str) { 38 | s = str.match(/[\w|\)][.?!](\s|$)/g); 39 | if (s == null) { 40 | return 0; 41 | } 42 | return s.length; 43 | } 44 | function updateView() { 45 | htmlOutput = md.render(editor.getValue()); 46 | var replacements = []; 47 | const rules = { 48 | "hardToRead": { 49 | "regex": /((\w+,\s+)|(\w+\s+)){20,}(\w+[\.|?|!])/g, 50 | "message": "This sentence is hard to read.", 51 | "color": "#f0f0a0", 52 | "summary": " sentences are hard to read.", 53 | "summarySingle": " sentence is hard to read." 54 | }, 55 | "adverbs": { 56 | "regex": /((\w+)ly)|sometimes|perhaps|maybe/g, 57 | "message": "Adverb: Use a forceful verb.", 58 | "color": "#a0e0ff", 59 | "summary": " adverbs.", 60 | "summarySingle": " adverb." 61 | }, 62 | "passiveVoice": { 63 | "regex": /\b((be(en)?)|(w(as|ere))|(is)|(a(er|m)))(\.|\,)?\s(\w+\s)?(\w+(en|ed))(\s|\.|\,)/g, 64 | "message": "Passive voice: Use active voice.", 65 | "color": "#a0f0a0", 66 | "summary": " uses of passive voice.", 67 | "summarySingle": " use of passive voice." 68 | }, 69 | "thereIsThereAre": { 70 | "regex": /\bThere (is|are)\b/g, 71 | "message": "Generic: Be precise.", 72 | "color": "#f0a0f0", 73 | "summary": " uses of There is/There are.", 74 | "summarySingle": " use of There is/There are." 75 | }, 76 | "genericVerb": { 77 | "regex": /\b(happen|occur)(s?)/g, 78 | "message": "Generic verb: Use precise verbs.", 79 | "color": "#f0a0f0", 80 | "summary": " uses of generic verbs.", 81 | "summarySingle": " use of generic verbs." 82 | } 83 | } 84 | var ruleReplacements = {} 85 | for (var label in rules) { 86 | rule = rules[label]; 87 | ruleReplacements[label] = 0; 88 | htmlOutput = htmlOutput.replaceAll(rule["regex"], function (match) { 89 | replacements.push(getPopover(match, rule["message"], rule["color"])); 90 | ruleReplacements[label]++; 91 | return getReplacementString(replacements.length - 1); 92 | }); 93 | } 94 | console.log(ruleReplacements); 95 | rulesSummary = ""; 96 | for (var label in rules) { 97 | var s = rules[label]["summary"]; 98 | if (ruleReplacements[label] == 1) { 99 | s = rules[label]["summarySingle"]; 100 | } 101 | rulesSummary += `
102 | ${ruleReplacements[label]}" 103 | ${s} 104 |
`; 105 | } 106 | document.getElementById('mdviewSummary').innerHTML = rulesSummary; 107 | for (let i = 0; i < replacements.length; i++) { 108 | htmlOutput = htmlOutput.replace(getReplacementString(i), replacements[i]); 109 | } 110 | htmlOutput = htmlOutput.replaceAll("
", `
`); 111 | htmlOutput = htmlOutput.replaceAll("", `
`); 112 | document.getElementById('mdview').innerHTML = htmlOutput; 113 | outputWithoutHtml = htmlOutput.replace(/(<([^>]+)>)/ig, ""); 114 | sentences = countSentences(outputWithoutHtml); 115 | words = countWords(outputWithoutHtml); 116 | characters = outputWithoutHtml.length - 1; 117 | if (characters < 0) { 118 | characters = 0; 119 | } 120 | warnings = replacements.length; 121 | document.getElementById('sentences').innerHTML = sentences; 122 | document.getElementById('words').innerHTML = words; 123 | document.getElementById('characters').innerHTML = characters; 124 | document.getElementById('warnings').innerHTML = warnings; 125 | warningsClass = "text-success"; 126 | if (warnings / words > 0.03) { 127 | warningsClass = "text-warning"; 128 | } 129 | if (warnings / words > 0.05) { 130 | warningsClass = "text-danger"; 131 | } 132 | document.getElementById('warnings').className = warningsClass; 133 | loadPopovers(); 134 | hljs.highlightAll(); 135 | addEventListener("beforeunload", beforeUnloadListener, { capture: true }); 136 | } 137 | updateView(); 138 | editor.session.on('change', function (delta) { updateView(); }); 139 | -------------------------------------------------------------------------------- /js/ts.min.js: -------------------------------------------------------------------------------- 1 | var editor=ace.edit("editor");function loadPopovers(){[].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')).map(function(e){return new bootstrap.Popover(e,{trigger:"hover focus",html:!0})})}function getPopover(e,r,t){return''+e+""}editor.setTheme("ace/theme/chrome"),editor.session.setMode("ace/mode/markdown"),editor.setOptions({fontSize:14,vScrollBarAlwaysVisible:!0,wrap:!0});var md=window.markdownit().use(window.markdownitFootnote);const beforeUnloadListener=e=>(e.preventDefault(),e.returnValue="Are you sure you want to exit?");function getReplacementString(e){return"[#r"+e+"]"}function countWords(e){let r=e.split(" ");return r.filter(e=>""!==e).length}function countSentences(e){return null==(s=e.match(/[\w|\)][.?!](\s|$)/g))?0:s.length}function updateView(){htmlOutput=md.render(editor.getValue());var e=[];let r={hardToRead:{regex:/((\w+,\s+)|(\w+\s+)){20,}(\w+[\.|?|!])/g,message:"This sentence is hard to read.",color:"#f0f0a0",summary:" sentences are hard to read.",summarySingle:" sentence is hard to read."},adverbs:{regex:/((\w+)ly)|sometimes|perhaps|maybe/g,message:"Adverb: Use a forceful verb.",color:"#a0e0ff",summary:" adverbs.",summarySingle:" adverb."},passiveVoice:{regex:/\b((be(en)?)|(w(as|ere))|(is)|(a(er|m)))(\.|\,)?\s(\w+\s)?(\w+(en|ed))(\s|\.|\,)/g,message:"Passive voice: Use active voice.",color:"#a0f0a0",summary:" uses of passive voice.",summarySingle:" use of passive voice."},thereIsThereAre:{regex:/\bThere (is|are)\b/g,message:"Generic: Be precise.",color:"#f0a0f0",summary:" uses of There is/There are.",summarySingle:" use of There is/There are."},genericVerb:{regex:/\b(happen|occur)(s?)/g,message:"Generic verb: Use precise verbs.",color:"#f0a0f0",summary:" uses of generic verbs.",summarySingle:" use of generic verbs."}};var t={};for(var n in r)rule=r[n],t[n]=0,htmlOutput=htmlOutput.replaceAll(rule.regex,function(r){return e.push(getPopover(r,rule.message,rule.color)),t[n]++,getReplacementString(e.length-1)});for(var n in console.log(t),rulesSummary="",r){var a=r[n].summary;1==t[n]&&(a=r[n].summarySingle),rulesSummary+='
'+t[n]+""+a+"
"}document.getElementById("mdviewSummary").innerHTML=rulesSummary;for(let o=0;o",'
')).replaceAll("
",'
'),document.getElementById("mdview").innerHTML=htmlOutput,sentences=countSentences(outputWithoutHtml=htmlOutput.replace(/(<([^>]+)>)/ig,"")),words=countWords(outputWithoutHtml),(characters=outputWithoutHtml.length-1)<0&&(characters=0),warnings=e.length,document.getElementById("sentences").innerHTML=sentences,document.getElementById("words").innerHTML=words,document.getElementById("characters").innerHTML=characters,document.getElementById("warnings").innerHTML=warnings,warningsClass="text-success",warnings/words>.03&&(warningsClass="text-warning"),warnings/words>.05&&(warningsClass="text-danger"),document.getElementById("warnings").className=warningsClass,loadPopovers(),hljs.highlightAll(),addEventListener("beforeunload",beforeUnloadListener,{capture:!0})}updateView(),editor.session.on("change",function(e){updateView()}); 2 | -------------------------------------------------------------------------------- /preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbrincoveanu/techscriptor/872aa442870df595b5dd5816f9688ce7c5d47610/preview.jpg -------------------------------------------------------------------------------- /site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /tests.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | These sentences should be rendered and highlighted correctly. 4 | 5 | ## Long sentences 6 | 7 | If you see a yellow highlight, your sentence is so dense and complicated that your readers will get lost trying to understand it and then you should consider splitting it up. 8 | 9 | ## Passive Voice 10 | 11 | Harry ate six shrimp at dinner. (active) 12 | At dinner, six shrimp were eaten by Harry. (passive) 13 | 14 | Beautiful giraffes roam the savannah. (active) 15 | The savannah is roamed by beautiful giraffes. (passive) 16 | 17 | Sue changed the flat tire. (active) 18 | The flat tire was changed by Sue. (passive) 19 | 20 | We are going to watch a movie tonight. (active) 21 | A movie is going to be watched by us tonight. (passive) 22 | 23 | I ran the obstacle course in record time. (active) 24 | The obstacle course was run by me in record time. (passive) 25 | 26 | The crew paved the entire stretch of highway. (active) 27 | The entire stretch of highway was paved by the crew. (passive) 28 | 29 | Mom read the novel in one day. (active) 30 | The novel was read by Mom in one day. (passive) 31 | 32 | be --- given. 33 | 34 | ”be beaten” and “were liked”, but not “between” or “be tenth” or “be a person who walked” 35 | 36 | The critic wrote a scathing review. (active) 37 | A scathing review was written by the critic. (passive) 38 | 39 | I will clean the house every Saturday. (active) 40 | The house will be cleaned by me every Saturday. (passive) 41 | 42 | The staff is required to watch a safety video every year. (active) 43 | A safety video will be watched by the staff every year. (passive) 44 | 45 | She faxed her application for a new job. (active) 46 | The application for a new job was faxed by her. (passive) 47 | 48 | Tom painted the entire house. (active) 49 | The entire house was painted by Tom. (passive) 50 | 51 | The teacher always answers the students’ questions. (active) 52 | The students’ questions are always answered by the teacher. (passive) 53 | 54 | The choir really enjoys that piece. (active) 55 | That piece is really enjoyed by the choir. (passive) 56 | 57 | Who taught you to ski? (active) 58 | By whom were you taught to ski? (passive) 59 | 60 | The forest fire destroyed the whole suburb. (active) 61 | The whole suburb was destroyed by the forest fire. (passive) 62 | 63 | The two kings are signing the treaty. (active) 64 | The treaty is being signed by the two kings. (passive) 65 | 66 | The cleaning crew vacuums and dusts the office every night. (active) 67 | Every night the office is vacuumed and dusted by the cleaning crew. (passive) 68 | 69 | Larry generously donated money to the homeless shelter. (active) 70 | Money was generously donated to the homeless shelter by Larry. (passive) 71 | 72 | No one responded to my sales ad. (active) 73 | My sales ad was not responded to by anyone. (passive) 74 | 75 | The wedding planner is making all the reservations. (active) 76 | All the reservations will be made by the wedding planner. (passive) 77 | 78 | Susan will bake two dozen cupcakes for the bake sale. (active) 79 | For the bake sale, two dozen cookies will be baked by Susan. (passive) 80 | 81 | The science class viewed the comet. (active) 82 | The comet was viewed by the science class. (passive) 83 | 84 | Who ate the last cookie? (active) 85 | The last cookie was eaten by whom? (passive) 86 | 87 | Alex posted the video on Facebook. (active) 88 | The video was posted on Facebook by Alex. (passive) 89 | 90 | The director will give you instructions. (active) 91 | Instructions will be given to you by the director. (passive) 92 | 93 | Thousands of tourists view the Grand Canyon every year. (active) 94 | The Grand Canyon is viewed by thousands of tourists every year. (passive) 95 | 96 | The homeowners remodeled the house to help it sell. (active) 97 | The house was remodeled by the homeowners to help it sell. (passive) 98 | 99 | The team will celebrate their victory tomorrow. (active) 100 | The victory will be celebrated by the team tomorrow. (passive) 101 | 102 | The saltwater eventually corroded the metal beams. (active) 103 | The metal beams were eventually corroded by the saltwater. (passive) 104 | 105 | The kangaroo carried her baby in her pouch. (active) 106 | The baby was carried by the kangaroo in her pouch. (passive) 107 | 108 | Some people raise sugar cane in Hawaii. (active) 109 | Sugar cane is raised by some people in Hawaii. (passive) 110 | 111 | ## Generic expressions 112 | 113 | There are all kinds of generic expressions. 114 | 115 | It can happen that your readers will find them difficult to interpret. 116 | 117 | Something occurred here. 118 | 119 | ## Adverbs 120 | 121 | Unfortunately, adverbs sometimes make technical readers bark loudly and ferociously. 122 | 123 | Perhaps or maybe. Sometimes and often. 124 | 125 | ## Special characters 126 | 127 | It learns to buy & hold for years, so we slowly drop the rewards to zero after 20 days of holding a position open. 128 | 129 | If you see a yellow highlight, your sentence is so dense and complicated; your readers will get lost trying to understand it and then you should consider splitting it up. 130 | --------------------------------------------------------------------------------