Skip to content

Commit

Permalink
VSC: Run rewrap on saving (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
stkb committed Feb 16, 2022
1 parent 035e577 commit 3b9b0fd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 23 deletions.
6 changes: 6 additions & 0 deletions vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
"default": false,
"description": "(EXPERIMENTAL) When wrapping lines, reformat paragraph indents."
},
"rewrap.onSave": {
"scope": "language-overridable",
"type": "boolean",
"default": false,
"description": "Do a rewrap on the whole document when saving."
},
"rewrap.autoWrap.enabled": {
"scope": "language-overridable",
"type": "boolean",
Expand Down
4 changes: 2 additions & 2 deletions vscode/src/AutoWrap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {getWrappingColumn, maybeAutoWrap} from './Core'
import {Memento, ThemeColor, workspace, window, TextDocumentChangeEvent, TextEditor, ConfigurationChangeEvent} from 'vscode'
import {buildEdit, catchErr, docLine, docType} from './Common'
import {buildEdits, catchErr, docLine, docType} from './Common'
import {EditorSettings, getCoreSettings, getEditorSettings} from './Settings'

/** Handler that's called if the text in the active editor changes */
Expand Down Expand Up @@ -30,7 +30,7 @@ const checkChange = async (e: TextDocumentChangeEvent) => {
// maybeAutoWrap does more checks: that newText isn't empty, but is only whitespace.
// Don't call this in a promise: it causes timing issues.
const edit = maybeAutoWrap(file, settings, newText, range.start, docLine(doc))
if (!edit.isEmpty) return editor.edit (builder => buildEdit(editor, builder, edit, false))
if (!edit.isEmpty) return editor.edit (builder => buildEdits(doc, edit, builder))
}
catch (err) { catchErr(err) }
}
Expand Down
35 changes: 20 additions & 15 deletions vscode/src/Common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {DocState, DocType, Edit, saveDocState} from './Core'
import vscode, {Position, Range, Selection, TextDocument, TextEditor, TextEditorEdit} from 'vscode'
import vscode, {Position, Range, TextDocument, TextEdit, TextEditor} from 'vscode'
import fd from 'fast-diff'
import GetCustomMarkers from './CustomLanguage'
const getCustomMarkers = GetCustomMarkers()
Expand All @@ -14,19 +14,20 @@ export const getDocState = (editor: TextEditor) : DocState => {
return {filePath: docType(doc).path, version: doc.version, selections}
}

/** Builds the vscode edits that apply an Edit to the document. Also calculates where the
* post-wrap selections will be and saves the state of the document. If the edit is empty
* this is a no-op */
export function buildEdit
(editor: TextEditor, editBuilder: TextEditorEdit, edit: Edit, saveState: boolean) : void
/** Creates and returns the vscode edits to be applied to the the document. If a
* TextEditorEdit is supplied, the edits are applied to it. If saveState is true, also
* calculates where the post-wrap selections will be and saves the state of the document.
* If the edit is empty this is a no-op */
export function buildEdits
(doc: TextDocument, edit: Edit, eb?: vscode.TextEditorEdit, saveState = false) : TextEdit[]
{
if (edit.isEmpty) return
const edits: TextEdit[] = []
if (edit.isEmpty) return edits

const doc = editor.document
, oldLines = Array(edit.endLine - edit.startLine + 1).fill(null)
.map((_, i) => doc.lineAt(edit.startLine + i).text)
const oldLines = Array(edit.endLine - edit.startLine + 1).fill(null)
.map((_, i) => doc.lineAt(edit.startLine + i).text)
, oldSelections = [...edit.selections].reverse()
, selections: Selection[] = []
, selections: vscode.Selection[] = []
let sel = oldSelections.pop()

const eol = doc.eol === vscode.EndOfLine.CRLF ? "\r\n" : "\n"
Expand All @@ -51,7 +52,7 @@ export function buildEdit
for (let [op, str] of diffs) {
if (op === fd.INSERT) {
offsetDiff += str.length
editBuilder.insert (startPos, str)
edits.push (TextEdit.insert (startPos, str))
continue
}

Expand All @@ -63,7 +64,7 @@ export function buildEdit
if (!newAnchorPos) newAnchorPos = checkSelPos(sel.anchor, newOffset)
if (!newActivePos) newActivePos = checkSelPos(sel.active, newOffset)
if (newAnchorPos && newActivePos) {
selections.push (new Selection (newAnchorPos, newActivePos))
selections.push (new vscode.Selection (newAnchorPos, newActivePos))
newAnchorPos = newActivePos = undefined
sel = oldSelections.pop ()
}
Expand All @@ -72,7 +73,7 @@ export function buildEdit

if (op === fd.DELETE) {
offsetDiff -= str.length
editBuilder.delete (new Range (startPos, endPos))
edits.push (TextEdit.delete (new Range (startPos, endPos)))
}
startPos = endPos
}
Expand All @@ -90,16 +91,20 @@ export function buildEdit
// Finish off selections after the edit
const lineDelta = edit.lines.length - (edit.endLine - edit.startLine + 1)
while (sel) {
selections.push (new Selection (
selections.push (new vscode.Selection (
newAnchorPos || sel.anchor.translate (lineDelta),
newActivePos || sel.active.translate (lineDelta)
))
newAnchorPos = newActivePos = undefined
sel = oldSelections.pop ()
}

if (eb)
edits.forEach(e => e.newText ? eb.insert (e.range.start, e.newText) : eb.delete (e.range))
if (saveState)
saveDocState ({filePath: doc.fileName, version: doc.version + 1, selections})

return edits
}

/** Catches any error and displays a friendly message to the user. */
Expand Down
29 changes: 23 additions & 6 deletions vscode/src/Extension.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import {maybeChangeWrappingColumn, rewrap} from './Core'
import {buildEdit, catchErr, docType, docLine, getDocState} from './Common'
import {ExtensionContext, TextEditor, TextEditorEdit, commands, window} from 'vscode'
import {getCoreSettings, getEditorSettings} from './Settings'
import {getWrappingColumn, maybeChangeWrappingColumn, rewrap} from './Core'
import {buildEdits, catchErr, docType, docLine, getDocState} from './Common'
import vscode, {TextEditor, TextEditorEdit, commands, window, workspace} from 'vscode'
import {getCoreSettings, getEditorSettings, getOnSaveSetting} from './Settings'
import AutoWrap from './AutoWrap'

export {activate, getCoreSettings, getEditorSettings}

/** Function to activate the extension. */
async function activate (context: ExtensionContext) {
async function activate (context: vscode.ExtensionContext) {
const autoWrap = AutoWrap(context.workspaceState, context.subscriptions)

// Register the commands
context.subscriptions.push
( commands.registerTextEditorCommand('rewrap.rewrapComment', rewrapCommentCommand)
, commands.registerTextEditorCommand('rewrap.rewrapCommentAt', rewrapCommentAtCommand)
, commands.registerTextEditorCommand('rewrap.toggleAutoWrap', autoWrap.editorToggle)
, workspace.onWillSaveTextDocument(onSaveDocument)
)
}

Expand Down Expand Up @@ -44,6 +45,22 @@ async function rewrapCommentAtCommand (editor: TextEditor, editBuilder: TextEdit
}


function onSaveDocument (e: vscode.TextDocumentWillSaveEvent) {
if (e.reason !== vscode.TextDocumentSaveReason.Manual) return
if (! getOnSaveSetting (e.document)) return
// We need an editor for the tab size. So for now we have to look for it.
const editor = window.visibleTextEditors.find(ed => ed.document === e.document)
if (!editor) return

const file = docType (e.document)
, settings = getCoreSettings (editor, cs => getWrappingColumn(file.path, cs))
, edit = rewrap (file, settings, [], docLine(e.document))
, edits = buildEdits (e.document, edit)

e.waitUntil(Promise.resolve(edits))
}


/** Collects the information for a wrap from the editor, passes it to the wrapping code,
* and then applies the result to the document. If an edit is applied, returns an updated
* DocState object, else returns null. Takes an optional customColumn to wrap at.
Expand All @@ -58,7 +75,7 @@ const doWrap = (editor: TextEditor, editBuilder: TextEditorEdit, customColumn?)
const selections = editor.selections

const edit = rewrap(docType(doc), settings, selections, docLine(doc))
buildEdit (editor, editBuilder, edit, isNaN(customColumn))
buildEdits (doc, edit, editBuilder, isNaN(customColumn))
}
catch (err) { catchErr(err) }
}
5 changes: 5 additions & 0 deletions vscode/src/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export function getEditorSettings (editor: TextEditor) : EditorSettings {
}
}

/** Gets the onSave setting for the wrap-on-save feature */
export function getOnSaveSetting (document: TextDocument) : boolean {
return workspace.getConfiguration('', document).get('rewrap.onSave', false)
}

const getAutoWrapSettings =
(config: WorkspaceConfiguration, lang: string) : AutoWrapSettings =>
({
Expand Down

0 comments on commit 3b9b0fd

Please sign in to comment.