├── .config ├── do.mjs └── dotnet-tools.json ├── .editorconfig ├── .github ├── FUNDING.yml ├── issue_template.md └── workflows │ └── main.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json ├── specs.code-snippets └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Rewrap.sln ├── core ├── Block.fs ├── Columns.fs ├── Core.Test.fsproj ├── Core.fsproj ├── Line.fs ├── Main.fs ├── Native.js ├── Nonempty.fs ├── Parsers.fs ├── Parsers_Markdown.fs ├── Parsers_RST.fs ├── Parsing.Comments.fs ├── Parsing.Core.fs ├── Parsing.DocComments.fs ├── Parsing.Documents.fs ├── Parsing.Language.fs ├── Parsing.Latex.fs ├── Parsing.Markdown.fs ├── Parsing.Sgml.fs ├── Parsing.SourceCode.fs ├── Parsing.fs ├── Parsing_Internal.fs ├── Parsing_SourceCode.fs ├── Prelude.fs ├── Selections.fs ├── Tests.fs ├── Types.fs ├── Wrapping.fs ├── package.json └── test │ └── package.json ├── do ├── do.cmd ├── docs ├── .nojekyll ├── README.md ├── configuration-visual-studio.md ├── configuration.md ├── extra-features.md ├── extra.css ├── images │ ├── autoWrapExample.gif │ ├── editToggleAutoWrap.png │ ├── example-smaller.png │ ├── example.png │ ├── example.svg │ ├── example1.png │ ├── example1.svg │ ├── logo.png │ ├── logo.svg │ ├── rulers1.gif │ ├── switch-to-prerelease.png │ ├── toggleAutoWrapCommand.png │ ├── vsAutoWrapStatusBar.png │ ├── vsc-autowrap-icon-off.png │ ├── vsc-autowrap-icon-on.png │ ├── vsc-autowrap-icon-tempoff.png │ ├── vsc-autowrap-icon-tempon.png │ ├── vsc-autowrap-settings.png │ ├── vscAutoWrapStatusBar.png │ ├── wcempty.png │ ├── wholeCommentFalse.png │ └── wholeCommentTrue.png ├── prerelease-versions.md └── specs │ ├── README.md │ ├── content-types │ ├── batch-file.md │ ├── d.md │ ├── dart.md │ ├── dotnet-xmldoc.md │ ├── doxygen.md │ ├── elixir.md │ ├── go.md │ ├── graphql.md │ ├── html.md │ ├── javadoc.md │ ├── julia.md │ ├── latex.md │ ├── lean.md │ ├── lua.md │ ├── markdown │ │ ├── _markdown.md │ │ ├── blockquotes.md │ │ ├── comments.md │ │ ├── fenced-code-blocks.md │ │ ├── footnotes.md │ │ ├── front-matter.md │ │ ├── headings.md │ │ ├── html.md │ │ ├── indented-code-blocks.md │ │ ├── linkrefdefs.md │ │ ├── lists.md │ │ ├── paragraphs.md │ │ ├── tables.md │ │ └── thematic-breaks.md │ ├── matlab.md │ ├── octave.md │ ├── plaintext.md │ ├── powershell.md │ ├── prolog.md │ ├── proto.md │ ├── python.md │ ├── r.md │ ├── restructuredtext │ │ ├── _restructuredtext.md │ │ ├── bullet-lists.md │ │ ├── doctest-blocks.md │ │ ├── enumerated-lists.md │ │ ├── field-lists.md │ │ ├── literal-blocks.md │ │ ├── narrow-section-titles.md │ │ ├── tables.md │ │ └── transitions-section-titles.md │ ├── tcl.md │ └── yaml.md │ └── features │ ├── block-comments.md │ ├── decoration-lines.md │ ├── double-sentence-spacing.md │ ├── embedded-sections.md │ ├── line-comments.md │ ├── misc.md │ ├── selections.md │ ├── spaces.md │ ├── special-characters.md │ └── tabs.md ├── mkdocs.yml ├── package-lock.json ├── package.json ├── vs ├── Commands.cs ├── Editor.cs ├── Options │ ├── OptionsPage.cs │ ├── OptionsPageControl.xaml │ ├── OptionsPageControl.xaml.cs │ └── ViewModel.cs ├── Properties │ └── AssemblyInfo.cs ├── RewrapPackage.cs ├── RewrapPackage.vsct ├── VS.csproj ├── VSPackage.resx ├── logo.png └── source.extension.vsixmanifest ├── vscode.test ├── fixture │ └── .vscode │ │ └── settings.json ├── run.cjs └── tests.cjs └── vscode ├── .eslintrc.yml ├── .vscodeignore ├── LICENSE ├── logo.png ├── package-lock.json ├── package.json ├── src ├── AutoWrap.ts ├── Common.ts ├── Core.ts ├── CustomLanguage.ts ├── Extension.ts ├── FixSelections.ts └── Settings.ts └── tsconfig.json /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fable": { 6 | "version": "3.6.2", 7 | "commands": [ 8 | "fable" 9 | ] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line=lf 3 | guidelines = 90 1px solid 77777799 4 | 5 | [*.{cs,fs}] 6 | indent_style = space 7 | insert_final_newline=true 8 | 9 | [*.cs] 10 | tab_width=4 11 | 12 | [*.fs] 13 | tab_width=2 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: stkb 2 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | # Triggers on pushes or pull requests to any branch 4 | on: [push, pull_request] 5 | 6 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 7 | jobs: 8 | build: 9 | # The type of runner that the job will run on 10 | runs-on: ubuntu-latest 11 | steps: 12 | 13 | - name: Check-out Repo # (under $GITHUB_WORKSPACE) 14 | uses: actions/checkout@v2 15 | 16 | - name: Setup .NET Core SDK 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 2.1.805 20 | 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: 14 25 | 26 | - name: Build & Run Tests 27 | uses: GabrielBB/xvfb-action@v1.2 28 | with: 29 | run: ./do build test --production 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vs/ 2 | *.csproj.user 3 | /.ionide/ 4 | /.vscode-test/ 5 | /.obj/ 6 | 7 | /node_modules/ 8 | /vscode/node_modules/ 9 | 10 | /core/dist/ 11 | /core/test/prod.js* 12 | /vscode/dist/ 13 | /vscode/README.md 14 | bin/ 15 | obj/ 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceFolder}/vscode" ], 11 | "sourceMaps": true, 12 | "preLaunchTask": "Build extension" 13 | }, 14 | { 15 | "name": "Core Tests", 16 | "type": "node", 17 | "request": "launch", 18 | "program": "${workspaceFolder}/core/test", 19 | "args": [], 20 | "preLaunchTask": "Build extension" 21 | }, 22 | { 23 | "name": "Extension Tests", 24 | "type": "extensionHost", 25 | "request": "launch", 26 | "runtimeExecutable": "${execPath}", 27 | "args": [ 28 | "--disable-extensions", 29 | "--extensionDevelopmentPath=${workspaceFolder}/vscode", 30 | "--extensionTestsPath=${workspaceFolder}/vscode.test/tests.cjs", 31 | "${workspaceFolder}/vscode.test/fixture" 32 | ], 33 | "sourceMaps": true, 34 | "preLaunchTask": "Build extension" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/.vs/": true, 5 | "node_modules/": true, 6 | "**/bin/*/net*": true, 7 | "**/obj/": true, 8 | "packages/": true, 9 | "fable": true 10 | }, 11 | "editor.rulers": [ 12 | { 13 | "column": 90, 14 | "color": "#7779" 15 | } 16 | ], 17 | "[markdown]": { 18 | "editor.rulers": [ 19 | { 20 | "column": 90, 21 | "color": "#7779" 22 | }, { 23 | "column": 38, 24 | "color": "#7773" 25 | }, 26 | { 27 | "column": 46, 28 | "color": "#7773" 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.vscode/specs.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Test (use overtype)": { 3 | "prefix": "test", 4 | "description": "Spec test template", 5 | "scope": "markdown", 6 | "body": [ 7 | " $0 ", 8 | " ", 9 | " ", 10 | " ", 11 | " ", 12 | " ", 13 | ] 14 | }, 15 | "Wrap marker": { 16 | "prefix": "wm", 17 | "description": "Wrap marker", 18 | "scope": "markdown", 19 | "body": "¦", 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "command": "./do build vscode", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "label": "Build extension", 12 | }, 13 | { 14 | "type": "shell", 15 | "command": "./do core test", 16 | "group": { 17 | "kind": "test", 18 | "isDefault": true 19 | }, 20 | "label": "Run core tests" 21 | }, 22 | { 23 | "type": "shell", 24 | "command": "./do watch", 25 | "group": "build", 26 | "isBackground": true, 27 | "label": "Watch", 28 | "problemMatcher": [] 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Steve Baker 2 | 3 | Unless explicitly stated otherwise all files in this repository are licensed 4 | under the Apache License, Version 2.0 (the "License"). You may not use these 5 | files except in compliance with the License. You may obtain a copy of the 6 | License at: 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software distributed 11 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 12 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | specific language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | For VS Code, 8 | Open VSX and 9 | 10 | Visual Studio.
11 | Latest stable version 1.16.3 / pre-release 17.x / 12 | changelog 13 |
14 | 15 | 16 |

Rewrap

17 | 18 |

19 | 20 | The main Rewrap command is: **Rewrap Comment / Text**, by default bound to 21 | `Alt+Q`. With the cursor in a comment block, hit this to re-wrap the contents to the 22 | [specified wrapping column](https://stkb.github.io/Rewrap/configuration/#wrapping-column). 23 | 24 | 25 | ## Features 26 | 27 | * Re-wrap comment blocks in many languages, with per-language settings. 28 | * Smart handling of contents, including Java-/JS-/XMLDoc tags and code examples. 29 | * Can select lines to wrap or multiple comments/paragraphs at once (even the whole 30 | document). 31 | * Also works with Markdown documents, LaTeX or any kind of plain text file. 32 | 33 | The contents of comments are usually parsed as markdown, so you can use lists, code 34 | samples (which are untouched) etc: 35 | 36 | 37 | 38 |

39 | See the docs site for more info.
40 | -------------------------------------------------------------------------------- /Rewrap.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.31912.275 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VS", "vs\VS.csproj", "{267305CC-3DBC-49F9-83ED-45F4F10E304D}" 6 | EndProject 7 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Core", "core\Core.fsproj", "{2AAAD6E7-324C-465F-83C3-A6B45C469C28}" 8 | EndProject 9 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Core.Test", "core\Core.Test.fsproj", "{24398B66-2AA9-4587-821E-0515E30B79B7}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6898F3AE-ED21-4B44-A75F-80588ACF451D}" 12 | ProjectSection(SolutionItems) = preProject 13 | .editorconfig = .editorconfig 14 | EndProjectSection 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {267305CC-3DBC-49F9-83ED-45F4F10E304D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {267305CC-3DBC-49F9-83ED-45F4F10E304D}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {267305CC-3DBC-49F9-83ED-45F4F10E304D}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {267305CC-3DBC-49F9-83ED-45F4F10E304D}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {2AAAD6E7-324C-465F-83C3-A6B45C469C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {2AAAD6E7-324C-465F-83C3-A6B45C469C28}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {2AAAD6E7-324C-465F-83C3-A6B45C469C28}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {2AAAD6E7-324C-465F-83C3-A6B45C469C28}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {24398B66-2AA9-4587-821E-0515E30B79B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {24398B66-2AA9-4587-821E-0515E30B79B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {24398B66-2AA9-4587-821E-0515E30B79B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {24398B66-2AA9-4587-821E-0515E30B79B7}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {EC87FBD9-F636-4979-B1AF-566318E24C92} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /core/Block.fs: -------------------------------------------------------------------------------- 1 | module Block 2 | 3 | open Prelude 4 | open Parsing_ 5 | 6 | 7 | module Wrappable = 8 | let inline mapPrefixes f x = Tuple.mapFirst f x 9 | let inline mapLines f x = Tuple.mapSecond f x 10 | let inline fromLines prefixes lines = (prefixes, lines) 11 | let toLines ((pHead: string, pTail: string), lines) = 12 | lines |> Nonempty.mapHead ((+) pHead) |> Nonempty.mapTail ((+) pTail) 13 | 14 | /// Takes a function (string -> string), parser (Lines -> Blocks) and Prefixes & 15 | /// Lines tuple. Uses the parser on the lines to make blocks. For each of these 16 | /// blocks, prepends the prefix from Prefixes for the corresponding line. Where 17 | /// wrapping produces new lines in a block, uses the function to transform the 18 | /// prefix for the first line of that block to something for new lines. 19 | let splitUp : (string -> string) -> (Nonempty -> Blocks) -> (Nonempty * Nonempty) -> Blocks = 20 | let concatPrefixes (h1, t1) (h2, t2) = h1 + h2, t1 + t2 21 | 22 | let prependPrefixTrimEndOfBlankLine (p: string) (s: string) : string = 23 | if Line.isBlank s then p.TrimEnd() else p + s 24 | 25 | fun makeDefPrefix -> 26 | /// Takes the remaining list of prefixes and the block that needs prefixes 27 | /// prepending. Removes prefixes from the list equal to the current size of 28 | /// the block. Returns that list, plus the prefixes to prepend to the head & 29 | /// tail lines of the block. 30 | let takePrefixes : Nonempty -> Block -> (string * string * Nonempty) = 31 | fun prefixes block -> 32 | let (Nonempty(p1, pBlockRest)), maybePRest = Nonempty.splitAt (size block) prefixes 33 | let pRest = maybePRest |? (singleton (List.tryLast pBlockRest |? p1)) 34 | p1, List.tryHead pBlockRest |? makeDefPrefix p1, pRest 35 | 36 | let prependPrefixes (prefixes, Nonempty(block, nextBlocks)) = 37 | let pre1, pre2, preNext = takePrefixes prefixes block 38 | let block' = 39 | match block with 40 | | Block.Comment _ -> // A comment in a comment (probably) won't happen :) 41 | block 42 | | Block.Wrap wrappable -> 43 | Block.Wrap (Wrappable.mapPrefixes (concatPrefixes (pre1, pre2)) wrappable) 44 | | Block.NoWrap ls -> 45 | ls 46 | |> Nonempty.mapHead (prependPrefixTrimEndOfBlankLine pre1) 47 | |> Nonempty.mapTail (prependPrefixTrimEndOfBlankLine pre2) 48 | |> Block.NoWrap 49 | | NBlock _ -> raise (System.Exception("splitUp on new block")) 50 | block', tuple preNext <<|> Nonempty.fromList nextBlocks 51 | 52 | fun parser (prefixes, lines) -> 53 | Nonempty.unfold prependPrefixes (prefixes, parser lines) 54 | 55 | /// Old version, for compatibility with old code 56 | let oldSplitUp : (Nonempty -> Blocks) -> Wrappable -> Blocks = 57 | fun parser ((pre1, pre2), lines) -> 58 | splitUp (always pre2) parser (Nonempty(pre1, [pre2]), lines) 59 | 60 | 61 | // Constructors 62 | 63 | let commentBlock : (Nonempty -> Blocks) -> Wrappable -> Block = 64 | fun parser wrappable -> Comment (oldSplitUp parser wrappable) 65 | let textBlock : Wrappable -> Block = Block.Wrap 66 | let ignoreBlock : Nonempty -> Block = Block.NoWrap 67 | -------------------------------------------------------------------------------- /core/Columns.fs: -------------------------------------------------------------------------------- 1 | module internal Columns 2 | 3 | // Deals the [wrapping to rulers 4 | // feature](https://stkb.github.io/Rewrap/#/settings-vscode#wrapping-to-rulers). 5 | // The user can have multiple "rulers" (potential wrapping columns) set, either 6 | // with the `editor.rulers` setting in VSCode or the Guidelines extension in VS. 7 | // 8 | // When a user opens a document the wrapping column is set at the first ruler in 9 | // their settings. To change it to the next ruler, they have to do a wrap 10 | // (alt+q) twice in a row without doing anything inbetween. 11 | // 12 | // If the user doesn't have any rulers set, we always treat it as if they have 13 | // a list of one ruler (their set wrapping column). 14 | 15 | open Rewrap 16 | open Prelude 17 | 18 | // We store one DocState record for the state of the editor after every wrap 19 | // operation, and then look at it again before the next. If it hasn't changed, 20 | // it's counted as doing a wrap twice in a row, and we move the wrapping column 21 | // to the next ruler (if it exists). 22 | // 23 | // The DocState stores the current filepath, document version number and 24 | // selection position, to make sure the user hasn't switched file, made an 25 | // edit, or even moved the cursor, respectively. 26 | let mutable private lastDocState : DocState = 27 | { filePath = ""; version = 0; selections = [||] } 28 | 29 | /// We remember the last wrapping column used for each document. 30 | let private docWrappingColumns = 31 | new System.Collections.Generic.Dictionary() 32 | 33 | 34 | //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv PUBLIC MEMBERS vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv// 35 | 36 | 37 | // Gets the current wrapping column for the given document, given the supplied 38 | // list of valid wrapping columns (rulers). The current wrapping column for the 39 | // document is stored, but this is only returned if it's contained in the given 40 | // list of rulers. 41 | // 42 | // If it isn't, then the user has changed these since the last wrap, so we just 43 | // pick the first one in the list. The most likely scenario here is simply that 44 | // the user changed the wrapping column in the settings, so we definitely want 45 | // to respect the new setting. 46 | // 47 | // The list of rulers must not be empty. 48 | let getWrappingColumn filePath rulers = 49 | let setAndReturn column = docWrappingColumns.[filePath] <- column; column 50 | let firstRuler = Array.head rulers 51 | if not (docWrappingColumns.ContainsKey(filePath)) then 52 | setAndReturn firstRuler 53 | else 54 | Array.tryFind ((=) docWrappingColumns.[filePath]) rulers 55 | |> Option.defaultValue firstRuler 56 | |> setAndReturn 57 | 58 | // Takes a set of rulers and check if we already have a wrapping column for the 59 | // given document. 60 | // 1) If we don't yet have one, the first ruler is used and that value is saved. 61 | // 2) If we do already have a wrapping column and it exists in the given rulers, 62 | // that value is returned. 63 | // 3) If the value we have isn't found in the given rulers, then the rulers must 64 | // have changed since we last wrapped. Like 1) we save and return the first 65 | // ruler 66 | // The list of rulers must not be empty 67 | let maybeChangeWrappingColumn (docState: DocState) (rulers: int[]) : int = 68 | let filePath = docState.filePath 69 | if not (docWrappingColumns.ContainsKey(filePath)) then 70 | getWrappingColumn filePath rulers 71 | else 72 | let shiftRulerIfDocStateUnchanged i = 73 | if docState = lastDocState then (i + 1) % rulers.Length else i 74 | let rulerIndex = 75 | Array.tryFindIndex ((=) docWrappingColumns.[filePath]) rulers 76 | |> maybe 0 shiftRulerIfDocStateUnchanged 77 | 78 | docWrappingColumns.[filePath] <- rulers.[rulerIndex] 79 | docWrappingColumns.[filePath] 80 | 81 | /// Saves the DocState, to compare against next time. 82 | let saveDocState docState = 83 | lastDocState <- docState 84 | -------------------------------------------------------------------------------- /core/Core.Test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | ..\.obj\.net\test 7 | ..\.obj\.net\test\bin 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /core/Core.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Rewrap.Core 6 | ..\.obj\.net\core 7 | ..\.obj\.net\core\bin 8 | 9 | 10 | 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 | -------------------------------------------------------------------------------- /core/Main.fs: -------------------------------------------------------------------------------- 1 | module Rewrap.Core 2 | 3 | open System 4 | open Prelude 5 | open Line 6 | open Parsing_ 7 | open Parsing.Language 8 | open Parsing.Documents 9 | open Columns 10 | 11 | // Re-exports from Columns 12 | let getWrappingColumn filePath rulers = getWrappingColumn filePath rulers 13 | let maybeChangeWrappingColumn docState rulers = maybeChangeWrappingColumn docState rulers 14 | let saveDocState docState = saveDocState docState 15 | 16 | /// Empty CustomMarkers object, for when no custom markers are supplied. 17 | let noCustomMarkers : CustomMarkers = {line = ""; block = ("","")} 18 | 19 | let languageNameForFile (file: File) : string = 20 | maybe null Language.name (languageForFile file) 21 | 22 | let languages : string[] = 23 | Seq.map Language.name Parsing.Documents.languages |> Seq.toArray 24 | 25 | /// The main rewrap function, to be called by clients 26 | let rewrap file settings selections (getLine: Func) = 27 | let processor = Parsing.Documents.select file 28 | let mkLine i = 29 | getLine.Invoke(i) |> Option.ofObj |> Option.map (fun l -> (l,i+1)) 30 | let strLines = Seq.unfold mkLine 0 |> Nonempty.fromSeqUnsafe // Can we get rid of this? 31 | let lines = strLines |> Seq.map (fun s -> Line("", s)) 32 | let ctx = Context(settings) 33 | processor ctx lines 34 | Selections.wrapSelected strLines selections ctx 35 | 36 | 37 | let strWidth tabSize str = Line.strWidth tabSize str 38 | 39 | /// The autowrap function, to be called by clients. Checks conditions and does 40 | /// an autowrap if all pass. 41 | /// 42 | /// The client must supply the new text that was inserted in the edit, as well 43 | /// as the position where it was inserted 44 | let maybeAutoWrap file settings newText (pos: Position) (getLine: Func) = 45 | let noEdit = Edit.empty 46 | 47 | if String.IsNullOrEmpty(newText) then noEdit 48 | // If column < 1 we never wrap 49 | elif settings.column < 1 then noEdit 50 | elif not (String.IsNullOrWhiteSpace(newText)) then noEdit else 51 | 52 | let enterPressed, indent = 53 | match newText.[0] with 54 | | '\r' -> true, newText.Substring(2) 55 | | '\n' -> true, newText.Substring(1) 56 | | _ -> false, "" 57 | if not enterPressed && newText.Length > 1 then noEdit else 58 | 59 | let line, char = 60 | pos.line, pos.character + (if enterPressed then 0 else newText.Length) 61 | let lineText = getLine.Invoke(line) 62 | let visualWidth = strWidth settings.tabWidth (String.takeStart char lineText) 63 | if visualWidth <= settings.column then noEdit else 64 | 65 | let fakeSelection = { 66 | anchor = { line = line; character = 0 } 67 | active = { line = line; character = lineText.Length } 68 | } 69 | // A getLine function that only gets lines up to & including where the 70 | // cursor is 71 | let wrappedGetLine = 72 | Func(fun i -> if i > line then null else getLine.Invoke(i)) 73 | rewrap file settings ([|fakeSelection|]) wrappedGetLine 74 | |> fun edit -> 75 | let afterPos = 76 | if enterPressed then { line = line + 1; character = indent.Length } 77 | else { line = line; character = char } 78 | edit.withSelections [| { anchor=afterPos; active=afterPos} |] 79 | -------------------------------------------------------------------------------- /core/Native.js: -------------------------------------------------------------------------------- 1 | import * as FS from 'fs' 2 | 3 | // Reads the contents of a file as an array of strings 4 | export const readFile = path => FS.readFileSync(path, {encoding: 'utf8'}).split(/\r?\n/) 5 | 6 | export const files = readSpecs(".") 7 | 8 | function readSpecs(dir) { 9 | if(FS.existsSync(dir + "/docs")) return readDir(dir + "/docs/specs") 10 | else return readSpecs(dir + "/..") 11 | } 12 | 13 | // Gets as an array the full paths of all *.md files in a dir and all subdirs 14 | function readDir (path) { 15 | const step = (acc, x) => { 16 | const fullName = path + "/" + x.name 17 | return x.isDirectory() ? [...acc, ...(readDir (fullName))] 18 | : fullName.endsWith(".md") ? [...acc, fullName] 19 | : acc 20 | } 21 | return FS.readdirSync(path, {withFileTypes: true}).reduce(step, []) 22 | } 23 | -------------------------------------------------------------------------------- /core/Nonempty.fs: -------------------------------------------------------------------------------- 1 | module Nonempty 2 | 3 | open Prelude 4 | 5 | let toList : 'a Nonempty -> 'a List = fun (Nonempty (h, t)) -> h :: t 6 | 7 | // ================ Creating ================ // 8 | 9 | // Duplicate for now so I don't have to modify old code 10 | let inline singleton x = x .@ [] 11 | 12 | let fromList : 'a List -> 'a Nonempty Option = 13 | function | [] -> None | x :: xs -> Some (x .@ xs) 14 | 15 | let fromSeqUnsafe : 'a seq -> 'a Nonempty = 16 | fun xs -> Seq.head xs .@ List.ofSeq (Seq.tail xs) 17 | 18 | let cons : 'a -> 'a Nonempty -> 'a Nonempty = 19 | fun h neList -> h .@ toList neList 20 | 21 | /// Appends an element to the end of the Nonempty list 22 | let snoc : 'a -> 'a Nonempty -> 'a Nonempty = 23 | fun last (Nonempty(h, t)) -> h .@ t @ [last] 24 | 25 | let append : 'a Nonempty -> 'a Nonempty -> 'a Nonempty = 26 | fun (Nonempty(h, t)) b -> h .@ t @ toList b 27 | 28 | let appendToList listA neListB = 29 | match fromList listA with 30 | | Some neListA -> append neListA neListB 31 | | None -> neListB 32 | 33 | // ================ Getting elements or other ================ // 34 | 35 | let head : 'a Nonempty -> 'a = fun (Nonempty (h, _)) -> h 36 | let tail : 'a Nonempty -> 'a List = fun (Nonempty (_, t)) -> t 37 | let last : 'a Nonempty -> 'a = 38 | fun (Nonempty (h, t)) -> fromMaybe h (List.tryLast t) 39 | 40 | let tryFind : ('a -> bool) -> 'a Nonempty -> 'a Option = 41 | fun predicate -> toList >> List.tryFind predicate 42 | 43 | 44 | // ================ Transforming ================ // 45 | 46 | let rev : 'a Nonempty -> 'a Nonempty = 47 | fun list -> list |> toList |> List.rev |> fromSeqUnsafe 48 | 49 | let mapHead : ('a -> 'a) -> 'a Nonempty -> 'a Nonempty = 50 | fun fn (Nonempty (h, t)) -> fn h .@ t 51 | 52 | let mapTail : ('a -> 'a) -> 'a Nonempty -> 'a Nonempty = 53 | fun fn (Nonempty (h, t)) -> h .@ List.map fn t 54 | 55 | let mapInit : ('a -> 'a) -> 'a Nonempty -> 'a Nonempty = 56 | fun fn -> rev >> mapTail fn >> rev 57 | 58 | let mapLast : ('a -> 'a) -> 'a Nonempty -> 'a Nonempty = 59 | fun fn -> rev >> mapHead fn >> rev 60 | 61 | let mapFold : ('s -> 'a -> 'b * 's) -> 's -> 'a Nonempty -> 'b Nonempty * 's = 62 | fun fn s (Nonempty (h,t)) -> 63 | let h', s' = fn s h in List.mapFold fn s' t |> Tuple.mapFirst ((.@) h') 64 | 65 | let replaceHead : 'a -> 'a Nonempty -> 'a Nonempty = 66 | fun h -> mapHead (fun _ -> h) 67 | 68 | let concatMap : ('a -> 'b Nonempty) -> 'a Nonempty -> 'b Nonempty = 69 | fun fn neList -> 70 | let rec loop output = function 71 | | [] -> output | x :: xs -> loop (append (fn x) output) xs 72 | rev neList |> (fun (Nonempty(head, tail)) -> loop (fn head) tail) 73 | 74 | /// Splits the list at the given position. If n is less than 1 then n = 1 75 | let splitAt : int -> 'a Nonempty -> ('a Nonempty * 'a Nonempty Option) = 76 | fun n (Nonempty(head, tail)) -> 77 | let rec loop count leftAcc maybeRightAcc = 78 | match maybeRightAcc with 79 | | None -> leftAcc, None 80 | | Some (Nonempty(x, xs)) -> 81 | if count < 1 then leftAcc, maybeRightAcc 82 | else loop (count - 1) (cons x leftAcc) (fromList xs) 83 | loop (n - 1) (singleton head) (fromList tail) |> Tuple.mapFirst rev 84 | 85 | /// Takes a predicate and a list, and Optionally returns a Tuple, of the longest 86 | /// prefix of the list for which the predicate holds, and the rest of the list. 87 | /// If that prefix is empty, returns None. 88 | let span : ('a -> bool) -> 'a Nonempty -> ('a Nonempty * 'a Nonempty Option) Option = 89 | fun predicate -> 90 | let rec loop output maybeRemaining = 91 | match maybeRemaining with 92 | | Some (Nonempty(h, t)) when predicate h -> loop (h :: output) (fromList t) 93 | | _ -> fromList (List.rev output) |> map (fun o -> o, maybeRemaining) 94 | Some >> loop [] 95 | 96 | /// Like span, but instead of a function that returns a bool, uses one that 97 | /// returns an Option. 98 | let spanMaybes : ('a -> 'b Option) -> 'a Nonempty -> ('b Nonempty * 'a Nonempty Option) Option = 99 | fun fn -> 100 | let rec loop output maybeRemaining = 101 | let inline finish () = fromList (List.rev output) |> map (fun o -> o, maybeRemaining) 102 | match maybeRemaining with 103 | | Some (Nonempty(h, t)) -> 104 | match fn h with Some x -> loop (x :: output) (fromList t) | None -> finish () 105 | | _ -> finish () 106 | Some >> loop [] 107 | 108 | /// Splits after the first element where the predicate evaluates true 109 | let splitAfter : ('a -> bool) -> 'a Nonempty -> 'a Nonempty * 'a Nonempty Option = 110 | fun predicate -> 111 | let rec loop output (Nonempty(h, t)) = 112 | match fromList t with 113 | | Some nextList when not (predicate h) -> loop (h :: output) nextList 114 | | x -> h .@ output, x 115 | loop [] >> Tuple.mapFirst rev 116 | 117 | let unfold : ('b -> 'a * 'b Option) -> 'b -> 'a Nonempty = 118 | fun fn -> 119 | let rec loop output input = 120 | match fn input with 121 | | (res, Some nextInput) -> loop (res :: output) nextInput 122 | | (res, None) -> Nonempty(res, output) 123 | loop [] >> rev 124 | -------------------------------------------------------------------------------- /core/Parsers.fs: -------------------------------------------------------------------------------- 1 | module internal Parsers 2 | 3 | open Prelude 4 | open Parsing_ 5 | open Parsing_Internal 6 | 7 | let ignoreAll : ContentParser = 8 | let rec parseLine line = pending line noWrapBlock (ThisLine << parseLine) 9 | fun _ctx -> parseLine 10 | 11 | let markdown ctx = Parsers_Markdown.markdown ctx 12 | 13 | let rst ctx = Parsers_RST.rst ctx 14 | -------------------------------------------------------------------------------- /core/Parsing.Comments.fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/core/Parsing.Comments.fs -------------------------------------------------------------------------------- /core/Parsing.DocComments.fs: -------------------------------------------------------------------------------- 1 | module internal Parsing.DocComments 2 | // This module deals with special formatting inside comments that are used for 3 | // documentation, eg javadoc, xmldoc, RDoc 4 | 5 | open Prelude 6 | open Rewrap 7 | open Block 8 | open Parsers 9 | open Parsing_ 10 | open Parsing.Core 11 | open Sgml 12 | open System.Text.RegularExpressions 13 | 14 | type private Lines = Nonempty 15 | 16 | let private markdown = Parsing.Markdown.markdown 17 | 18 | /// Splits lines into sections which start with lines matching the given regex. 19 | /// For each of those sections, the matchParser is applied to turn the lines 20 | /// into blocks. If the first section doesn't start with a matching line, it is 21 | /// processed with the noMatchParser. 22 | let private splitBeforeTags 23 | : Regex -> (Match -> Settings -> string TotalParser) -> (Settings -> string TotalParser) 24 | -> Settings -> Lines -> Blocks = 25 | fun regex matchParser noMatchParser settings (Nonempty(outerHead, outerTail)) -> 26 | 27 | let rec prependRev (Nonempty(head, tail)) maybeRest = 28 | let nextRest = maybeRest |> maybe (singleton head) (Nonempty.cons head) 29 | Nonempty.fromList tail |> maybe nextRest (fun next -> prependRev next (Some nextRest)) 30 | 31 | let rec loop (tagMatch: Match) buffer maybeOutput lines = 32 | let parser = if tagMatch.Success then matchParser tagMatch else noMatchParser 33 | let addBufferToOutput () = prependRev (parser settings (Nonempty.rev buffer)) maybeOutput 34 | match lines with 35 | | [] -> (addBufferToOutput ()) |> Nonempty.rev 36 | | headLine :: tailLines -> 37 | let m = regex.Match(headLine) 38 | let nextTagMatch, nextBuffer, nextOutput = 39 | if m.Success then m, singleton headLine, Some (addBufferToOutput ()) 40 | else tagMatch, Nonempty.cons headLine buffer, maybeOutput 41 | loop nextTagMatch nextBuffer nextOutput tailLines 42 | 43 | loop (regex.Match(outerHead)) (Nonempty.singleton outerHead) None outerTail 44 | 45 | let javadoc = 46 | let tagRegex = Regex(@"^\s*@(\w+)(.*)$") 47 | 48 | /// "Freezes" inline tags ({@tag }) so that they don't get broken up 49 | let inlineTagRegex = Regex(@"{@[a-z]+.*?[^\\]}", RegexOptions.IgnoreCase) 50 | let markdownWithInlineTags settings = 51 | let replaceSpace (m: Match) = m.Value.Replace(' ', '\000') 52 | map (fun s -> inlineTagRegex.Replace(s, replaceSpace)) >> markdown settings 53 | 54 | let matchParser (m: Match) = 55 | if Line.isBlank (m.Groups.Item(2).Value) then 56 | if m.Groups.Item(1).Value.ToLower() = "example" then 57 | (fun _ -> ignoreBlock >> singleton) 58 | else ignoreFirstLine markdownWithInlineTags 59 | else markdownWithInlineTags 60 | 61 | splitBeforeTags tagRegex matchParser markdownWithInlineTags 62 | 63 | 64 | /// DartDoc has just a few special tags. We keep lines beginning with these 65 | /// unwrapped. 66 | let dartdoc = 67 | let tagRegex = Regex(@"^\s*(@nodoc|{@template|{@endtemplate|{@macro)") 68 | splitBeforeTags tagRegex (fun _ -> ignoreFirstLine markdown) markdown 69 | 70 | 71 | let psdoc = 72 | let tagRegex = Regex(@"^\s*\.([A-Z]+)") 73 | let codeLineRegex = Regex(@"^\s*PS C:\\>") 74 | 75 | let exampleSection settings lines = 76 | let trimmedExampleSection = 77 | ignoreFirstLine (splitBeforeTags codeLineRegex (fun _ -> ignoreFirstLine markdown) markdown) 78 | match Nonempty.span Line.isBlank lines with 79 | | Some (blankLines, None) -> Nonempty.singleton (ignoreBlock blankLines) 80 | | Some (blankLines, Some remaining) -> 81 | Nonempty.cons (ignoreBlock blankLines) (trimmedExampleSection settings remaining) 82 | | None -> trimmedExampleSection settings lines 83 | 84 | splitBeforeTags tagRegex 85 | (fun m -> 86 | if m.Groups.Item(1).Value = "EXAMPLE" then ignoreFirstLine exampleSection else 87 | ignoreFirstLine 88 | (fun settings -> 89 | Comments.extractWrappable "" false (fun _ -> " ") settings 90 | >> Block.oldSplitUp (markdown settings) 91 | ) 92 | ) 93 | markdown 94 | 95 | 96 | /// DDoc for D. Stub until it's implemented. https://dlang.org/spec/ddoc.html 97 | let ddoc = 98 | markdown 99 | 100 | 101 | /// Godoc comments use a godoc compatible parser instead of markdown. 102 | /// Here, any lines that are indented more than the "standard" aren't wrapped. 103 | /// 104 | /// https://blog.golang.org/godoc-documenting-go-code 105 | let godoc settings = 106 | let indentedLines = 107 | ignoreParser (Nonempty.span (fun line -> line.[0] = ' ' || line.[0] = '\t')) 108 | let textLines = 109 | splitIntoChunks (afterRegex (Regex(" $"))) >> map (Wrap << Wrappable.fromLines ("", "")) 110 | 111 | textLines 112 | |> takeUntil (tryMany [blankLines; indentedLines]) 113 | |> repeatToEnd 114 | 115 | let xmldoc = 116 | let blank = docOf ignoreAll 117 | let blockTags = 118 | [| "code"; "description"; "example"; "exception"; "include" 119 | "inheritdoc"; "list"; "listheader"; "item"; "para"; "param" 120 | "permission"; "remarks"; "seealso"; "summary"; "term"; "typeparam" 121 | "typeparamref"; "returns"; "value" 122 | |] 123 | sgml blank blank blockTags 124 | -------------------------------------------------------------------------------- /core/Parsing.Language.fs: -------------------------------------------------------------------------------- 1 | module internal Parsing.Language 2 | 3 | open System 4 | open Parsing_ 5 | 6 | type Language = 7 | private Language of string * array * array * DocumentProcessor 8 | 9 | module Language = 10 | 11 | // Takes 4 args to create a Language: 12 | // 1. display name (used only in VS) 13 | // 2. string of aliases (language IDs used by the client. Not needed if 14 | // they only differ from display name by casing) 15 | // 3. string of file extensions (including `.`). Used to give support to 16 | // files that are not known by the client. 17 | // 4. document processor 18 | // 19 | // Aliases and extensions are separated by `|` 20 | let create (name: string) (aliases: string) (exts: string) parser : Language = 21 | let split (s: string) = s.ToLower().Split([|'|'|], StringSplitOptions.RemoveEmptyEntries) 22 | Language(name, Array.append [|name.ToLower()|] (split aliases), split exts, parser) 23 | 24 | let name (Language(n,_,_,_)) = n 25 | 26 | let parser (Language(_,_,_,p)) = p 27 | 28 | let matchesFileLanguage (fileLang: string) (Language(_,ids,_,_)) = 29 | Seq.contains (fileLang.ToLower()) ids 30 | 31 | let matchesFilePath (path: string) (Language(_,_,exts,_)) = 32 | let fileName = path.ToLower().Split('\\', '/') |> Array.last 33 | let tryMatch : string -> bool = function 34 | | ext when ext.StartsWith(".") -> fileName.EndsWith(ext) 35 | | fullname -> fileName.Equals(fullname) 36 | 37 | Seq.exists tryMatch exts 38 | -------------------------------------------------------------------------------- /core/Parsing.Sgml.fs: -------------------------------------------------------------------------------- 1 | module internal Parsing.Sgml 2 | 3 | open Prelude 4 | open Line 5 | open Parsing_ 6 | open Block 7 | open Rewrap 8 | open Parsing.Core 9 | open System.Text.RegularExpressions 10 | 11 | let private markdown = Parsing.Markdown.markdown 12 | 13 | let inline private regex str = 14 | Regex(str, RegexOptions.IgnoreCase) 15 | 16 | let private scriptMarkers = 17 | (regex "") 18 | 19 | let private cssMarkers = 20 | (regex "") 21 | 22 | let sgml 23 | (scriptParser: DocumentProcessor) 24 | (cssParser: DocumentProcessor) 25 | (blockTags: seq) 26 | (settings: Settings) 27 | : TotalParser = 28 | 29 | let embeddedScript (markers: Regex * Regex) (contentParser: DocumentProcessor) = 30 | let runContent lines : Blocks = 31 | let ctx = Context(settings) 32 | contentParser ctx (Seq.map (fun (l: string) -> Line("", l)) lines) 33 | ctx.getBlocks () 34 | 35 | let afterFirstLine _ lines = 36 | let (Nonempty(lastLine, initLinesRev)) = Nonempty.rev lines 37 | if (snd markers).IsMatch(lastLine) then 38 | match Nonempty.fromList (List.rev initLinesRev) with 39 | | Some middleLines -> 40 | Nonempty.snoc 41 | (ignoreBlock (Nonempty.singleton (Nonempty.last lines))) 42 | (runContent middleLines) 43 | | None -> 44 | Nonempty.singleton <| ignoreBlock (Nonempty.singleton (Nonempty.last lines)) 45 | else runContent lines 46 | 47 | optionParser 48 | (takeLinesBetweenMarkers markers) 49 | (ignoreFirstLine afterFirstLine settings) 50 | 51 | let otherParsers = 52 | tryMany 53 | [ blankLines 54 | Comments.blockComment 55 | markdown ( "", "" ) ( "" ) settings 56 | embeddedScript scriptMarkers (scriptParser) 57 | embeddedScript cssMarkers (cssParser) 58 | ] 59 | 60 | // Checks if a regex contains a block tag name as its first captured group 61 | let isBlockTag (pattern: string) (line: string) = 62 | let m = (regex pattern).Match(line) 63 | m.Success && (Seq.isEmpty blockTags || 64 | Seq.contains (m.Groups.[1].Value.ToLower()) blockTags) 65 | 66 | let beginsWithBlockStartTag = isBlockTag @"^\s*<([\w.-]+)" 67 | let justBlockEndTag = isBlockTag @"^\s*" 68 | let endsWithBlockTag = isBlockTag @"([\w.-]+)>\s*$" 69 | 70 | let breakBefore line = justBlockEndTag line || beginsWithBlockStartTag line 71 | let breakAfter line = 72 | Line.contains (regex @"([""\s]>\s*| )$") line || endsWithBlockTag line 73 | 74 | let paragraphBlocks = 75 | splitIntoChunks (splitBefore breakBefore) 76 | >> Nonempty.concatMap (splitIntoChunks (Nonempty.splitAfter breakAfter)) 77 | >> map (indentSeparatedParagraphBlock textBlock) 78 | 79 | takeUntil otherParsers paragraphBlocks |> repeatToEnd 80 | -------------------------------------------------------------------------------- /core/Parsing.SourceCode.fs: -------------------------------------------------------------------------------- 1 | module internal Parsing.SourceCode 2 | 3 | open Rewrap 4 | open Block 5 | open Core 6 | open Sgml 7 | open Parsing_ 8 | 9 | let private markdown = Parsing.Markdown.markdown 10 | 11 | /// Creates a parser for source code files, given a list of comment parsers 12 | let oldSourceCode : List OptionParser> -> DocumentProcessor = 13 | fun commentParsers -> 14 | 15 | toNewDocProcessor <| fun settings -> 16 | let commentParsers = tryMany (List.map (fun cp -> cp settings) commentParsers) 17 | let codeParser = (ignoreBlock >> Nonempty.singleton) 18 | takeUntil commentParsers codeParser |> repeatToEnd 19 | 20 | 21 | /// Line comment parser that takes a custom content parser 22 | let customLine = 23 | Comments.lineComment 24 | 25 | /// A standard line comment parser with the given pattern 26 | let oldLine : string -> Settings -> OptionParser = 27 | customLine markdown 28 | 29 | /// Block comment parser that takes a custom content parser and middle line prefixes 30 | let customBlock = 31 | Comments.blockComment 32 | 33 | /// A standard block comment parser with the given start and end patterns 34 | let oldBlock : (string * string) -> Settings -> OptionParser = 35 | customBlock markdown ("", "") 36 | 37 | 38 | /// C-Style line comment parser (//) 39 | let cLine = 40 | oldLine "//" 41 | 42 | /// C-Style block comment parser (/* ... */) 43 | let cBlock = 44 | customBlock markdown ("*", "") (@"/\*", @"\*/") 45 | 46 | /// Markers for javadoc 47 | let javadocMarkers = 48 | (@"/\*[*!]", @"\*/") 49 | 50 | 51 | /// Parser for java/javascript (also used in html) 52 | let java : DocumentProcessor = 53 | oldSourceCode 54 | [ customBlock DocComments.javadoc ( "*", " * " ) javadocMarkers 55 | cBlock 56 | customLine DocComments.javadoc "//[/!]" 57 | cLine 58 | ] 59 | 60 | /// Parser for css (also used in html) 61 | let css : DocumentProcessor = java 62 | 63 | /// Parser for html 64 | let html : DocumentProcessor = 65 | toNewDocProcessor <| sgml java css [||] 66 | -------------------------------------------------------------------------------- /core/Parsing_Internal.fs: -------------------------------------------------------------------------------- 1 | /// Helper functions and types for the parsers 2 | module internal Parsing_Internal 3 | 4 | open System 5 | open System.Text.RegularExpressions 6 | open Prelude 7 | open Line 8 | open Parsing_ 9 | 10 | 11 | //---------- Operations On Lines -------------------------------------------------------// 12 | 13 | 14 | /// Creates a regex ignores case & is ecmascript compatible 15 | let inline regex pat = Regex(pat, RegexOptions.IgnoreCase ||| RegexOptions.ECMAScript) 16 | 17 | 18 | let tryMatch' : Regex -> Line -> Option = 19 | fun rx line -> 20 | let m = Line.onContent(fun s -> rx.Match(s)) line 21 | if not m.Success then None else 22 | Some (Array.ofSeq <| seq { for g in m.Groups -> g.Value }, line) 23 | 24 | /// Try matching the given regex on the line's content. If no match returns 25 | /// None. If there is a match, returns a string array where [0] is the whole 26 | /// match, [1] the first group etc. 27 | let tryMatch : Regex -> Line -> string array option = 28 | fun rx -> tryMatch' rx >> map fst 29 | 30 | /// Like tryMatch, but just returns the match's success 31 | let isMatch : Regex -> Line -> bool = fun rx -> tryMatch' rx >> Option.isSome 32 | 33 | 34 | /// If the line (after the prefix) is just whitespace 35 | let isBlankLine : Line -> bool = Line.onContent String.IsNullOrWhiteSpace 36 | 37 | // Gets the number of initial blank spaces on the line, from the current prefix 38 | // position 39 | let indentLength : Line -> int = Line.onContent (fun s -> s.Length - s.TrimStart().Length) 40 | 41 | // Adjusts the line splitpoint so that all whitespace is trimmed from the content 42 | let trimWhitespace : Line -> Line = 43 | Line.trimUpTo Int32.MaxValue 44 | 45 | 46 | //---------- Line Results --------------------------------------------------------------// 47 | 48 | 49 | // Constructs a pending result 50 | let pending line blockType nextParser = 51 | Pending (LineRes(line, blockType, false, nextParser)) 52 | 53 | /// Constructs a Finished result with a next parser 54 | let finished line blockType nextParser = 55 | Finished (LineRes(line, blockType, false, Some nextParser)) 56 | 57 | /// Constructs a Finished result without a next parser 58 | let finished_ line blockType = 59 | Finished (LineRes(line, blockType, false, None)) 60 | 61 | 62 | /// Takes a line result and "wraps" its parser in a function that takes the inner 63 | /// parser and returns a new one. This is for when we want to either modify the 64 | /// line before it's passed to the inner parser, or modify the result coming out 65 | /// of it. 66 | let wrapResultParser : ('a -> 'b) -> LineRes<'a> -> LineRes<'b> = 67 | fun p r -> LineRes<'b>(r.line, r.blockType, r.isDefault, p r.nextParser) 68 | 69 | 70 | /// Creates a PrefixTransformer that starts at the start point, removes the given number 71 | /// of chars, and inserts the given number of spaces + the extra string 72 | let blankOut' start remove spaces extra : PrefixTransformer = 73 | let rep = String (' ', spaces) 74 | fun pre -> pre.Substring(0, start) + rep + extra + pre.Substring(start + remove) 75 | 76 | /// Creates a PrefixTransformer from a given Line and length, that blanks out `length` 77 | /// characters, starting at the line's split position. 78 | let blankOut (line: Line) (length: int) : PrefixTransformer = 79 | blankOut' line.split length length "" 80 | 81 | 82 | /// Adds a default prefix function to a FirstLineRes 83 | let wrapPrefixFn prefixFn = 84 | let inline f (r: LineRes<'p>) = 85 | let blockType = 86 | match r.blockType with 87 | | :? WrapBlock as b -> wrapBlock' (prefixFn << b.prefixFn) 88 | | _ -> r.blockType 89 | LineRes(r.line, blockType, r.isDefault, r.nextParser) 90 | function | Pending r -> Pending (f r) | Finished r -> Finished (f r) 91 | 92 | 93 | let voidParseLine = fun _ -> () 94 | 95 | 96 | //---------- Common Parsers ------------------------------------------------------------// 97 | 98 | 99 | /// Function that takes a Context and Line and tries a new parser on it 100 | type TryNewParser = Context -> Line -> Option 101 | 102 | 103 | /// Line content is empty or whitespace. nonText would cover this as well but this 104 | /// separate case is added for performance 105 | let blankLine : TryNewParser = 106 | fun _ctx line -> if isBlankLine line then Some (finished_ line noWrapBlock) else None 107 | 108 | 109 | /// A line that doesn't have any text on it. Always stops after 1 line since there may be 110 | /// other blocks that begin with non-text lines (eg " 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | rewrap 16 | -------------------------------------------------------------------------------- /docs/images/rulers1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/rulers1.gif -------------------------------------------------------------------------------- /docs/images/switch-to-prerelease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/switch-to-prerelease.png -------------------------------------------------------------------------------- /docs/images/toggleAutoWrapCommand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/toggleAutoWrapCommand.png -------------------------------------------------------------------------------- /docs/images/vsAutoWrapStatusBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsAutoWrapStatusBar.png -------------------------------------------------------------------------------- /docs/images/vsc-autowrap-icon-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsc-autowrap-icon-off.png -------------------------------------------------------------------------------- /docs/images/vsc-autowrap-icon-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsc-autowrap-icon-on.png -------------------------------------------------------------------------------- /docs/images/vsc-autowrap-icon-tempoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsc-autowrap-icon-tempoff.png -------------------------------------------------------------------------------- /docs/images/vsc-autowrap-icon-tempon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsc-autowrap-icon-tempon.png -------------------------------------------------------------------------------- /docs/images/vsc-autowrap-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vsc-autowrap-settings.png -------------------------------------------------------------------------------- /docs/images/vscAutoWrapStatusBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/vscAutoWrapStatusBar.png -------------------------------------------------------------------------------- /docs/images/wcempty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/wcempty.png -------------------------------------------------------------------------------- /docs/images/wholeCommentFalse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/wholeCommentFalse.png -------------------------------------------------------------------------------- /docs/images/wholeCommentTrue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkb/Rewrap/6a27098e20baf95759c621a5cc1cd9b3fedbd9ed/docs/images/wholeCommentTrue.png -------------------------------------------------------------------------------- /docs/prerelease-versions.md: -------------------------------------------------------------------------------- 1 | ## Pre-release versions 2 | 3 | Pre-release versions are used to test or hammer-out new features before they're added to 4 | the stable branch. Releases are more frequent and features come earlier to these versions. 5 | 6 | You can switch to the Pre-Release channel from within VS Code (v1.63 or above) by going to 7 | the extension's page and then choosing **Switch to Pre-Release Version** (make sure to hit 8 | "Reload Required" after it's installed). 9 | 10 | 11 | 12 | You don't have to be using VS Code Insiders to do this. You can painlessly switch back to 13 | the stable channel at any time too. [More info at the VS Code docs]( 14 | https://code.visualstudio.com/updates/v1_63#_pre-release-extensions). 15 | 16 | 17 | ### Visual Studio 18 | 19 | Visual Studio doesn't have the above VS Code feature, but you can still download the .vsix 20 | file from the **Assets** section below, and install it manually by double-clicking the 21 | file. When the next major stable version is released, VS will automatically upgrade to 22 | that. 23 | -------------------------------------------------------------------------------- /docs/specs/README.md: -------------------------------------------------------------------------------- 1 | # Specs 2 | 3 | The files in these folders use examples to document Rewrap's capabilities and specify 4 | expected results. They are all in markdown format. 5 | 6 | The examples also serve as tests. 7 | 8 | 9 | ## Test layout 10 | 11 | An example test -> An example ¦ 12 | ¦ test ¦ 13 | 14 | Each test, set into an indented code block, consists of 2 sections, or columns, with the input 15 | on the left and and the expected output after the `->` marker. 16 | 17 | ### Wrapping column 18 | 19 | The wrapping column in each sample is represented by `¦` character. It's required to appear 20 | on at least one line in the input and output sections (though for asthetics should appear 21 | on all lines where it can). It's checked that all `¦` markers are at the same column. 22 | 23 | ### Selections 24 | 25 | Most samples are treated as if they're wholly selected, though where selections are 26 | explicitly needed, they're marked with `«»` characters, with `«` as the anchor point and 27 | `»` as the active point. These are removed from the input. 28 | 29 | ### Whitespace 30 | 31 | Tabs are represented by `-→`. Where explicitly needed, spaces are represented by a `·` 32 | (U+00B7). All lines are assumed to have no trailing whitespace unless these characters are 33 | used. 34 | 35 | ## Settings 36 | 37 | Settings for a test are given in a blockquote, on one line, separated by commas and 38 | colons. They apply to all subsequent tests until a new settings block is given. Eg: 39 | 40 | > language: javascript, tabSize: 2, doubleSentenceSpacing: true 41 | 42 | When absent, the default values are used: 43 | - tabSize: 4 44 | - doubleSentenceSpacing: false 45 | - language: plaintext 46 | - wrapWholeComment: true 47 | - reformat: false 48 | 49 | A new settings block completely replaces the previous one; any absent values are reset 50 | back to defaults. 51 | 52 | 53 | ## Writing tests 54 | 55 | If writing tests in VS Code, the first thing that's highly recommended is the [Overtype 56 | ](https://marketplace.visualstudio.com/items?itemName=DrMerfy.overtype) extension, which 57 | gives a toggle to type in overtype mode with the 'ins' key. 58 | 59 | This project folder also has extra guideline rulers at columns 38 & 46 in markdown files, 60 | as well as a couple of code snipets. 61 | 62 | Steps: 63 | 64 | 1. Press ctrl+space and select the `test` snippet. This will insert lines of spaces and 65 | put the cursor at the correct starting position. 66 | 2. Using overtype mode, add the input text. Use the cursor keys to move down a line rather 67 | than enter. 68 | 3. Add `¦` characters on all lines to indicate the wrapping column (you can copy this char 69 | from another test or use the `wm` snippet). You can do this quicker by first using 70 | ctrl+alt+down to make an insertion point on each line. 71 | 4. Select all the input as a block by using ctrl+alt+up/down to select vertically, and 72 | shift+left/right to select horizontally. Copy, press End to move the insertion points 73 | to the end of each line (should be on col 47, at the 2nd ruler) and paste. 74 | 5. Adjust input & output text as needed. 75 | 6. On one of the lines, add a `->` marker (dash + greater than) at col 39 (the first 76 | ruler). This can be on any line but for asthetics should be on/near the middle line of 77 | the block 78 | 79 | ### Additional notes/tips 80 | 81 | - Though not outright banned, tests with identical input and output are discouraged, since 82 | something not working at all will produce a false pass. In tests where a non-wrapping 83 | block is expected, try to include another block that should wrap (eg. normal paragraph 84 | text). 85 | - Tests are easier to see (and edit) if input and output contain the same amount of lines. 86 | When adding wrapped sections, try to add lines that will be split and lines that will be 87 | joined, to keep the number of lines after wrapping (almost) the same. 88 | - When running tests, examples can be singled-out by adding the tag `` at the end of 89 | any of their lines. 90 | -------------------------------------------------------------------------------- /docs/specs/content-types/batch-file.md: -------------------------------------------------------------------------------- 1 | # Batch/Cmd files 2 | 3 | > language: "batch file" 4 | 5 | Comments lines start with `REM` (or `@REM`). This is case-insensitive. 6 | 7 | rem text ¦ -> rem text text ¦ 8 | rem text text ¦ rem text ¦ 9 | 10 | REM text ¦ -> REM text text ¦ 11 | REM text text ¦ REM text ¦ 12 | 13 | Rem text ¦ -> Rem text text ¦ 14 | Rem text text ¦ Rem text ¦ 15 | 16 | @rem text ¦ -> @rem text text ¦ 17 | @rem text text ¦ @rem text ¦ 18 | 19 | While not strictly a comment marker, `::` is also often used for comments, so is also 20 | supported. 21 | 22 | :: text ¦ -> :: text text ¦ 23 | :: text text ¦ :: text ¦ 24 | -------------------------------------------------------------------------------- /docs/specs/content-types/d.md: -------------------------------------------------------------------------------- 1 | > language: "d" 2 | 3 | // a b c -> // a b ¦ 4 | // d ¦ // c d ¦ 5 | /* a b c /* a b ¦ 6 | d ¦ c d ¦ 7 | */ ¦ */ ¦ 8 | /+ a b c /+ a b ¦ 9 | d ¦ c d ¦ 10 | +/ ¦ +/ ¦ 11 | x x x x x x x x x x 12 | 13 | /// a b c -> /// a b ¦ 14 | /// d ¦ /// c d ¦ 15 | -------------------------------------------------------------------------------- /docs/specs/content-types/dart.md: -------------------------------------------------------------------------------- 1 | # Dart 2 | 3 | DartDoc comments can be in the form `///` or `/** ... */`. It generally does not 4 | use tags, except for a few special ones: @nodoc, @template, @endtemplate and 5 | @macro. 6 | 7 | > language: "dart" 8 | 9 | /// aaaaaaaa ¦ -> /// aaaaaaaa bbbbbb ¦ 10 | /// bbbbbb c ¦ /// c 11 | /// @nodoc ¦ /// @nodoc 12 | /// dddddddd ¦ /// dddddddd eeeeee 13 | /// eeeeee f ¦ /// f 14 | /// {@template a} ¦ /// {@template a} 15 | /// gggggggg ¦ /// gggggggg hhhhhh 16 | /// hhhhhh i ¦ /// i 17 | /// {@endtemplate} ¦ /// {@endtemplate} 18 | /// jjjjjjjj ¦ /// jjjjjjjj kkkkkk 19 | /// kkkkkk l ¦ /// l 20 | /// {@macro a} ¦ /// {@macro a} 21 | /// mmmmmmmm ¦ /// mmmmmmmm nnnnnn 22 | /// nnnnnn o ¦ /// o 23 | -------------------------------------------------------------------------------- /docs/specs/content-types/dotnet-xmldoc.md: -------------------------------------------------------------------------------- 1 | # .Net XmlDoc 2 | 3 | .Net xml doc comments are parsed as xml/html instead of markdown, so wrapping and newlines 4 | follow the rules for html instead. This keeps tags separated on new lines. 5 | 6 | > language: "csharp" 7 | 8 | /// What the method -> /// What the ¦ 9 | /// does ¦ /// method does ¦ 10 | /// ¦ /// ¦ 11 | /// The s param ¦ /// The s param ¦ 12 | /// ¦ /// ¦ 13 | /// ¦ /// ¦ 14 | /// Extended info. ¦ /// Extended info. Text ¦ 15 | /// Text text text. ¦ /// text text. ¦ 16 | /// ¦ /// ¦ 17 | 18 | > language: "fsharp" 19 | 20 | /// What the method -> /// What the ¦ 21 | /// does ¦ /// method does ¦ 22 | /// ¦ /// ¦ 23 | /// The s param ¦ /// The s param ¦ 24 | /// ¦ /// ¦ 25 | /// ¦ /// ¦ 26 | /// Extended info. ¦ /// Extended info. Text ¦ 27 | /// Text text text. ¦ /// text text. ¦ 28 | /// ¦ /// ¦ 29 | 30 | > language: "vb" 31 | 32 | ''' What the method -> ''' What the ¦ 33 | ''' does ¦ ''' method does ¦ 34 | ''' ¦ ''' ¦ 35 | ''' The s param ¦ ''' The s param ¦ 36 | ''' ¦ ''' ¦ 37 | ''' ¦ ''' ¦ 38 | ''' Extended info. ¦ ''' Extended info. Text ¦ 39 | ''' Text text text. ¦ ''' text text. ¦ 40 | ''' ¦ ''' ¦ 41 | 42 | There is however, an extra rule. Line breaks are only preserved before/after certain 43 | "block tags". The list of these tags is: *code, description, example, exception, include, 44 | inheritdoc, list, listheader, item, para, param, permission, remarks, seealso, summary, 45 | term, typeparam, typeparamref, returns, value*. 46 | 47 | All other tags, eg `` are treated as inline tags and will always be wrapped inline with 48 | the surrounding text. 49 | 50 | > language: "csharp" 51 | 52 | /// Text ¦ /// Text TypeName text ¦ 53 | /// TypeName ¦ -> /// text. ¦ 54 | /// text text. ¦ ¦ 55 | -------------------------------------------------------------------------------- /docs/specs/content-types/doxygen.md: -------------------------------------------------------------------------------- 1 | > language: "c++" 2 | 3 | /** ¦ -> /** ¦ 4 | * a b c * a b ¦ 5 | * @t d e * c ¦ 6 | */ ¦ * @t d¦ 7 | * e ¦ 8 | */ ¦ 9 | 10 | /*! ¦ -> /*! ¦ 11 | * a b c * a b ¦ 12 | * @t d e * c ¦ 13 | */ ¦ * @t d¦ 14 | * e ¦ 15 | */ ¦ 16 | 17 | //! a b c -> //! a b ¦ 18 | //! @t d¦e //! c ¦ 19 | ¦ //! @t d¦ 20 | ¦ //! e ¦ 21 | 22 | Triple-slash comments expect xml (https://www.doxygen.nl/manual/xmlcmds.html) 23 | 24 | /// What the method -> /// What the ¦ 25 | /// does ¦ /// method does ¦ 26 | /// ¦ /// ¦ 27 | /// The s param ¦ /// The s param ¦ 28 | /// ¦ /// ¦ 29 | /// ¦ /// ¦ 30 | /// Extended info. ¦ /// Extended info. Text ¦ 31 | /// Text text text. ¦ /// text text. ¦ 32 | /// ¦ /// ¦ 33 | -------------------------------------------------------------------------------- /docs/specs/content-types/elixir.md: -------------------------------------------------------------------------------- 1 | > language: "elixir" 2 | 3 | Elixir has '#' line comments. 4 | 5 | x x x x -> x x x x 6 | # a b c # a b ¦ 7 | # d ¦ # c d ¦ 8 | x x ¦ x x ¦ 9 | x x ¦ x x ¦ 10 | 11 | It also has @doc, @moduledoc and @typedoc comments 12 | 13 | x x x x x x x x x -> x x x x x x x x x 14 | @doc """ ¦ @doc """ ¦ 15 | a b c d e f g h i a b c d e f g ¦ 16 | j k ¦ h i j k ¦ 17 | """ ¦ """ ¦ 18 | x x ¦ x x ¦ 19 | x x ¦ x x ¦ 20 | 21 | 22 | x x x x x x x x x -> x x x x x x x x x 23 | @moduledoc """¦ @moduledoc """¦ 24 | a b c d e f g h i a b c d e f g ¦ 25 | j k ¦ h i j k ¦ 26 | """ ¦ """ ¦ 27 | x x ¦ x x ¦ 28 | x x ¦ x x ¦ 29 | 30 | x x x x x x x x x -> x x x x x x x x x 31 | @typedoc """ ¦ @typedoc """ ¦ 32 | a b c d e f g h i a b c d e f g ¦ 33 | j k ¦ h i j k ¦ 34 | """ ¦ """ ¦ 35 | x x ¦ x x ¦ 36 | x x ¦ x x ¦ 37 | -------------------------------------------------------------------------------- /docs/specs/content-types/go.md: -------------------------------------------------------------------------------- 1 | > language: "go", tabWidth: 2 2 | 3 | Go uses [tabs](../features/tabs.md) for indentation. Comments in are wrapped using the 4 | rules of the godoc parser. There are two simple rules. 5 | 6 | Paragraphs are separated by blank lines. 7 | 8 | // paragraph one -> // paragraph ¦ 9 | // text ¦ // one text ¦ 10 | // ¦ // ¦ 11 | // paragraph two // paragraph ¦ 12 | // text ¦ // two text ¦ 13 | 14 | Pre-formatted text must be indented relative to the surrounding comment text. 15 | 16 | // normal wrapped paragraph -> // normal wrapped ¦ 17 | // text ¦ // paragraph text ¦ 18 | // preformatted { // preformatted { 19 | // text ¦ // text ¦ 20 | // } ¦ // } ¦ 21 | // normal wrapped paragraph // normal wrapped ¦ 22 | // text ¦ // paragraph text ¦ 23 | 24 | Unlike in markdown: 25 | * No blank line is required between a normal paragraph and indented code block. 26 | * Any amount of indent (as little as a 1-space) is required for the indented 27 | code block, as opposed to 4 spaces. 28 | 29 | Example 30 | 31 | // a b ¦ -> // a b c ¦ 32 | // c d ¦ // d ¦ 33 | // a ¦ // a ¦ 34 | // b ¦ // b ¦ 35 | // a b ¦ // a b c ¦ 36 | // c d ¦ // d ¦ 37 | 38 | Tabs also work for indented sections within the comment. However to keep things simple, 39 | Rewrap replaces tabs within comments with the visually equivalent number of spaces. (Godoc 40 | also does something weird with tabs: if any line contains a tab before the text, then 41 | those lines, plus all lines with a 2 or more space indent, are treated as preformatted 42 | text.) 43 | 44 | // a b ¦ -> // a b c ¦ 45 | // c d ¦ // d ¦ 46 | // →a ¦ // a ¦ 47 | // →b ¦ // b ¦ 48 | // a b ¦ // a b c ¦ 49 | // c d ¦ // d ¦ 50 | -------------------------------------------------------------------------------- /docs/specs/content-types/graphql.md: -------------------------------------------------------------------------------- 1 | > language: "graphql" 2 | 3 | GraphQL supports comments (`#`) and Block Strings (`"""`). Comments are always 4 | single-line, while Block Strings are inherently multi-line. Block Strings are 5 | typically used to provide descriptions (documentation) for GraphQL types and 6 | operations. 7 | 8 | ## Comments 9 | 10 | # a b c d e f g h i -> # a b c d¦ 11 | ¦ # e f g h¦ 12 | ¦ # i ¦ 13 | 14 | ## Block Strings 15 | 16 | """ ¦ -> """ ¦ 17 | a b c d e f g h i a b c d e¦ 18 | """ ¦ f g h i ¦ 19 | ¦ """ ¦ 20 | 21 | type Mutation { ¦ type Mutation { ¦ 22 | """ ¦ """ ¦ 23 | This is a long line. -> This is a ¦ 24 | """ ¦ long line. ¦ 25 | loginUser() String """ ¦ 26 | } ¦ loginUser() String 27 | ¦ } ¦ 28 | 29 | foo { ¦ foo { ¦ 30 | bar(baz: """ bar(baz: """ 31 | a b c d e f -> a b c d e¦ 32 | g h ¦ f g h ¦ 33 | """) ¦ """) ¦ 34 | } ¦ } ¦ 35 | -------------------------------------------------------------------------------- /docs/specs/content-types/html.md: -------------------------------------------------------------------------------- 1 | # HTML 2 | 3 | > language: "html" 4 | 5 | Html (includes Xml) has its own wrapping behaviors. Unlike other source code 6 | files, all content is considered equal when it comes to selections; selecting a 7 | whole document will wrap comments and other content alike. 8 | 9 | ¦ 11 | ¦ text text ¦ 12 | ¦ text text ¦ 13 | 14 | Mixed content on a line is wrapped as one. 15 | 16 | one two -> one ¦ 17 | ¦ two ¦ 18 | 19 | Any line that starts with a tag starts a new paragraph. 20 | 21 | Foo bar -> Foo ¦ 22 |
¦ bar ¦ 23 | ¦
¦ 24 | 25 | Any line that ends with a tag ends the paragraph. 26 | 27 |
¦
¦ 28 | aaa bbbbbbb -> aaa ¦ 29 | cc bbbbbbb cc¦ 30 | 31 | This means things like this are preserved. 32 | 33 |
    ¦
      ¦ 34 |
    • 1
    • ¦ ->
    • 1
    • ¦ 35 |
    • 2
    • ¦
    • 2
    • ¦ 36 |
    ¦
¦ 37 | 38 | 39 | ## Embedded JS and CSS 40 | 41 | If the selection within an HTML document contains script or style tags, the 42 | contents of those tags are wrapped using the rules for javascript or css. 43 | 44 | ¦ ¦ 49 | 50 | ¦ ¦ 58 | 59 | ¦ -> ¦ 60 | // one two three // one two ¦ 61 | // four ¦ three // four¦ 62 | ¦ ¦ 63 | -------------------------------------------------------------------------------- /docs/specs/content-types/julia.md: -------------------------------------------------------------------------------- 1 | > language: "julia" 2 | 3 | Julia is like Python with slight differences. There are only triple double-quote 4 | strings (no triple single-quote). These can effectively be preceeded by any 5 | user-defined string 6 | 7 | r"""¦ -> r"""¦ 8 | a b c a b ¦ 9 | d ¦ c d ¦ 10 | """ ¦ """ ¦ 11 | 12 | doc"""¦ -> doc""" 13 | a b c d a b c ¦ 14 | e ¦ d e ¦ 15 | """ ¦ """ ¦ 16 | 17 | This can also be preceeded by the `@doc` macro 18 | 19 | @doc raw"""¦ -> @doc raw""" 20 | a b c d e f g a b c d e f 21 | h i ¦ g h i ¦ 22 | """ ¦ """ ¦ 23 | 24 | 25 | It also has `#` line comments and `#= =#` block comments. 26 | 27 | # a b c -> # a b ¦ 28 | # d ¦ # c d ¦ 29 | 30 | #= ¦ #= ¦ 31 | a b c -> a b ¦ 32 | d ¦ c d ¦ 33 | =# ¦ =# ¦ 34 | -------------------------------------------------------------------------------- /docs/specs/content-types/lean.md: -------------------------------------------------------------------------------- 1 | > language: "lean" 2 | 3 | -- a b c -> -- a b ¦ 4 | -- d ¦ -- c d ¦ 5 | x x x x x x x x x x 6 | /- a b c /- a b ¦ 7 | d ¦ c d ¦ 8 | -/ ¦ -/ ¦ 9 | x x x x x x x x x x 10 | -------------------------------------------------------------------------------- /docs/specs/content-types/lua.md: -------------------------------------------------------------------------------- 1 | > language: "lua" 2 | 3 | Line comment 4 | 5 | --a b c -> --a b ¦ 6 | --d ¦ --c d ¦ 7 | z y x w z y x w 8 | v u ¦ v u ¦ 9 | 10 | Block comment 11 | 12 | --[[ --[[ 13 | a b c -> a b ¦ 14 | d ¦ c d ¦ 15 | ]]-- ¦ ]]-- ¦ 16 | z y x w z y x w 17 | v u ¦ v u ¦ 18 | 19 | 20 | Block comment markers can have any number or '='s in them as long as they match 21 | 22 | --[=[ --[=[ 23 | a b c -> a b ¦ 24 | d ¦ c d ¦ 25 | ]=]-- ¦ ]=]-- ¦ 26 | z y x w z y x w 27 | v u ¦ v u ¦ 28 | 29 | --[=====[ --[=====[ 30 | a b c -> a b ¦ 31 | d ¦ c d ¦ 32 | ]=====]-- ]=====]-- 33 | z y x w z y x w 34 | v u ¦ v u ¦ 35 | -------------------------------------------------------------------------------- /docs/specs/content-types/markdown/blockquotes.md: -------------------------------------------------------------------------------- 1 | > language: "markdown" 2 | 3 | Issue 204 4 | 5 | > a ¦ > a ¦ 6 | > ¦ -> > ¦ 7 | > a b c > a b¦ 8 | > d ¦ > c d¦ 9 | 10 | Content can have varying prefixes. With reformat off this needs to be preserved. 11 | 12 | > a ¦ > a ¦ 13 | > ¦ -> > ¦ 14 | >a b c >a b ¦ 15 | >d ¦ >c d ¦ 16 | 17 | > a ¦ > a ¦ 18 | > ¦ -> > ¦ 19 | >a b c >a b ¦ 20 | >d ¦ >c d ¦ 21 | > ¦ > ¦ 22 | > a ¦ > a ¦ 23 | 24 | > a ¦ > a ¦ 25 | > ¦ -> > ¦ 26 | >>a b c >>a b¦ 27 | d ¦ c d ¦ 28 | > ¦ > ¦ 29 | >>> a¦ >>> a¦ 30 | 31 | > a ¦ > a ¦ 32 | >>> a b -> >>> a¦ 33 | >>> ¦ >>> b¦ 34 | >>> ¦ 35 | 36 | If the input is only 1 line, then created lines are given the same prefix as the 37 | first 38 | 39 | > a b c d e f -> > a b c ¦ 40 | ¦ > d e f ¦ 41 | 42 | >a b c d e f -> >a b c ¦ 43 | ¦ >d e f ¦ 44 | 45 | > a b c d e f -> > a b c ¦ 46 | ¦ > d e f ¦ 47 | 48 | If the a line doesn't start with the `>` marker, then the blockquote section has 49 | terminated, unless the line is a paragraph continuation line. 50 | 51 | >··``` ¦ >··``` ¦ 52 | ···foo ¦ -> ···foo ¦ 53 | ··bar ¦ ··bar baz¦ 54 | ··baz ¦ ¦ 55 | 56 | The first (optional) space after the `>` marker is treated as part of the 57 | blockquote marker, so an indented code block has to be indented 5 spaces. 58 | 59 | >·····code block >·····code block 60 | >····text ¦ -> >····text text¦ 61 | >····text ¦ ¦ 62 | 63 | > one·· ¦ > one·· ¦ 64 | two ¦ -> two three¦ 65 | > three four > four 66 | -------------------------------------------------------------------------------- /docs/specs/content-types/markdown/comments.md: -------------------------------------------------------------------------------- 1 | # Markdown: Comments 2 | 3 | > language: markdown 4 | 5 | Comments are detected but wrapping their contents is not yet supported. 6 | 7 | ¦ ¦ 8 | ¦ -> --> ¦ 12 | ¦ ¦ 13 | new ¦ new paragraph ¦ 14 | paragraph ¦ ¦ 15 | -------------------------------------------------------------------------------- /docs/specs/content-types/markdown/fenced-code-blocks.md: -------------------------------------------------------------------------------- 1 | > language: "markdown" 2 | 3 | A fenced code block begins with at least 3 backticks or at least 3 tildes. 4 | 5 | ``` ¦ -> ``` ¦ 6 | code ¦ code ¦ 7 | block ¦ block ¦ 8 | 9 | ~~~ ¦ -> ~~~ ¦ 10 | code ¦ code ¦ 11 | block ¦ block ¦ 12 | 13 | Fewer than 3 doesn't start a code block. 14 | 15 | `` ¦ -> `` no code¦ 16 | no ¦ block ¦ 17 | code block¦ ¦ 18 | 19 | ~~ ¦ -> ~~ no code¦ 20 | no ¦ block ¦ 21 | code block¦ ¦ 22 | 23 | As with other markdown they may be indented up to inc 3 spaces. 24 | 25 | ···~~~ ¦ -> ···~~~ ¦ 26 | code code ¦ 27 | block block ¦ 28 | 29 | 4 or more and it doesn't count. 30 | 31 | ····~~~ ¦ -> ····~~~ ¦ 32 | Not in a code block Not in a ¦ 33 | code block¦ 34 | 35 | If the opening code fence is tildes, any characters may follow it. 36 | 37 | ~~~a bc`~`~ ~~~a bc`~`~ 38 | code ¦ code ¦ 39 | block ¦ -> block ¦ 40 | ~~~ ¦ ~~~ ¦ 41 | one ¦ one two ¦ 42 | two ¦ ¦ 43 | 44 | If the opening code fence is backticks, any characters except backticks may 45 | follow it. 46 | 47 | ```a bc~~~~ ```a bc~~~~ 48 | code ¦ code ¦ 49 | block ¦ -> block ¦ 50 | ``` ¦ ``` ¦ 51 | text ¦ text text ¦ 52 | text ¦ ¦ 53 | 54 | If there are backticks following, then the text between is treated as inline code 55 | instead. 56 | 57 | ```a bc``¦ ```a bc`` 58 | not in ¦ -> not in a ¦ 59 | a code ¦ code ¦ 60 | block ¦ block ¦ 61 | 62 | The closing fence must use the same character as the opening fence. 63 | 64 | ``` ¦ ``` ¦ 65 | code ¦ code ¦ 66 | block ¦ block ¦ 67 | ~~~ ¦ ~~~ ¦ 68 | still ¦ -> still ¦ 69 | in code block in code block 70 | ``` ¦ ``` ¦ 71 | outside code outside ¦ 72 | block ¦ code block¦ 73 | 74 | ~~~ ¦ ~~~ ¦ 75 | code ¦ code ¦ 76 | block ¦ block ¦ 77 | ``` ¦ ``` ¦ 78 | still ¦ -> still ¦ 79 | in code block in code block 80 | ~~~ ¦ ~~~ ¦ 81 | outside code outside ¦ 82 | block ¦ code block¦ 83 | 84 | If the closing fence is indented more than 3 spaces (relative to the container 85 | not the opening fence), it doesn't count. 86 | 87 | ··``` ¦ ··``` ¦ 88 | code ¦ code ¦ 89 | block ¦ block ¦ 90 | ····``` ¦ ····``` ¦ 91 | still ¦ -> still ¦ 92 | in code block in code block 93 | ``` ¦ ``` ¦ 94 | outside code outside ¦ 95 | block ¦ code block¦ 96 | 97 | If the opening fence is 4 or more characters, the closing fence must be as least 98 | as long. 99 | 100 | ~~~~ ¦ ~~~~ ¦ 101 | code ¦ code ¦ 102 | block ¦ block ¦ 103 | ~~~ ¦ ~~~ ¦ 104 | still ¦ -> still ¦ 105 | in code block in code block 106 | ~~~~~ ¦ ~~~~~ ¦ 107 | outside code outside ¦ 108 | block ¦ code block¦ 109 | 110 | If there is anything other than whitespace on the line after the closing fence, 111 | it doesn't count. 112 | 113 | ``` ¦ ``` ¦ 114 | code ¦ code ¦ 115 | block ¦ block ¦ 116 | ``` abc ¦ ``` abc ¦ 117 | still ¦ -> still ¦ 118 | in code block in code block 119 | ``` ¦ ``` ¦ 120 | outside code outside ¦ 121 | block ¦ code block¦ 122 | -------------------------------------------------------------------------------- /docs/specs/content-types/markdown/footnotes.md: -------------------------------------------------------------------------------- 1 | > language: "markdown" 2 | 3 | Footnotes are not to be confused with [link reference definitions](linkrefdefs.md). 4 | 5 | Footnotes are not in the CommonMark spec, but are supported by a variety of other flavors 6 | of markdown, including GFM, (PHP) Markdown Extra, MultiMarkdown and Pandoc. These all have 7 | slight differences, so until Rewrap offers support for multiple markdown specs, it tries 8 | to cover everything as best it can. 9 | 10 | A footnote is a section beginning with `[^