Skip to content

Commit

Permalink
perf: minimize edited content in a file (#77)
Browse files Browse the repository at this point in the history
* perf: minimize edited content in a file

* test: add a test case of modifier reduce function
  • Loading branch information
ktsn committed Dec 2, 2018
1 parent 1d5b376 commit 1eb1e94
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 31 deletions.
9 changes: 9 additions & 0 deletions src/parser/modifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ export function modify(code: string, modfiers: Modifiers): string {
return loop('', 0, ms[0], ms.slice(1))
}

export function reduce<T>(
modifiers: Modifiers,
fn: (acc: T, modifier: Modifier) => T,
initial: T
): T {
const ms = flatten(modifiers).sort(modifierComperator)
return ms.reduce(fn, initial)
}

function modifierComperator(a: Modifier, b: Modifier): number {
if (a.pos < b.pos) {
return -1
Expand Down
34 changes: 17 additions & 17 deletions src/repositories/vue-file-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ import {
} from '../parser/style/modify'

export interface VueFileRepositoryFs {
/**
* Read a file content as string.
*/
readFile(uri: string): Promise<string>
writeFile(uri: string, code: string): Promise<void>

/**
* Modify a file with specified modifiers.
*/
modifyFile(uri: string, modifiers: Modifiers): Promise<void>
}

export class VueFileRepository extends EventEmitter {
Expand Down Expand Up @@ -74,7 +81,7 @@ export class VueFileRepository extends EventEmitter {
? existingComponent.name
: component.name

const modifier: Modifiers = [
const modifiers: Modifiers = [
insertToTemplate(
target.template,
path,
Expand All @@ -83,7 +90,7 @@ export class VueFileRepository extends EventEmitter {
]

if (!existingComponent) {
modifier.push(
modifiers.push(
insertComponentScript(
target.script,
target.code,
Expand All @@ -93,8 +100,7 @@ export class VueFileRepository extends EventEmitter {
)
}

const updated = modify(target.code, modifier)
this.save(uri, updated)
this.modify(uri, target.code, modifiers)
}

addStyleDeclaration(
Expand All @@ -108,9 +114,7 @@ export class VueFileRepository extends EventEmitter {
}

const { code, styles } = file
const added = modify(code, [insertDeclaration(styles, declaration, path)])

this.save(uri, added)
this.modify(uri, code, [insertDeclaration(styles, declaration, path)])
}

removeStyleDeclaration(uri: string, path: number[]): void {
Expand All @@ -120,9 +124,7 @@ export class VueFileRepository extends EventEmitter {
}

const { code, styles } = file
const removed = modify(code, [removeDeclaration(styles, path)])

this.save(uri, removed)
this.modify(uri, code, [removeDeclaration(styles, path)])
}

updateStyleDeclaration(
Expand All @@ -135,9 +137,7 @@ export class VueFileRepository extends EventEmitter {
}

const { code, styles } = file
const updated = modify(code, [updateDeclaration(styles, declaration)])

this.save(uri, updated)
this.modify(uri, code, [updateDeclaration(styles, declaration)])
}

on(event: 'update', fn: (vueFile: VueFile) => void): this {
Expand All @@ -152,14 +152,14 @@ export class VueFileRepository extends EventEmitter {
this.files.set(uri, parseVueFile(code, uri))
}

private save(uri: string, code: string): void {
this.set(uri, code)
private modify(uri: string, prevCode: string, modifiers: Modifiers): void {
this.set(uri, modify(prevCode, modifiers))

const vueFile = this.get(uri)!
assert(vueFile, `VueFile object '${uri}' not found after saving it`)
this.emit('update', vueFile)

// We don't wait until the file is saved
this.fs.writeFile(uri, code)
this.fs.modifyFile(uri, modifiers)
}
}
35 changes: 35 additions & 0 deletions src/vscode/modifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as vscode from 'vscode'
import { Modifiers, reduce } from '../parser/modifier'

export async function applyModifiers(
uri: string,
modifiers: Modifiers
): Promise<void> {
const parsedUri = vscode.Uri.parse(uri)
const doc = await vscode.workspace.openTextDocument(parsedUri)

const wsEdit = reduce(
modifiers,
(edit, m) => {
switch (m.type) {
case 'Add':
edit.insert(parsedUri, doc.positionAt(m.pos), m.value)
break
case 'Remove':
edit.delete(
parsedUri,
new vscode.Range(
doc.positionAt(m.pos),
doc.positionAt(m.pos + m.length)
)
)
break
default:
}
return edit
},
new vscode.WorkspaceEdit()
)

await vscode.workspace.applyEdit(wsEdit)
}
15 changes: 3 additions & 12 deletions src/vue-designer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import debounce from 'lodash.debounce'
import { startStaticServer, startWebSocketServer } from './server/main'
import { AssetResolver } from './asset-resolver'
import { Watcher } from './vscode/watcher'
import { applyModifiers } from './vscode/modifier'
import { VueFileRepository } from './repositories/vue-file-repository'
import { SettingRepository } from './repositories/setting-repository'
import { EditorRepository } from './repositories/editor-repository'
Expand Down Expand Up @@ -53,18 +54,8 @@ async function createVueFileRepository(): Promise<VueFileRepository> {
return document.getText()
},

async writeFile(uri, code) {
const parsedUri = vscode.Uri.parse(uri)
const doc = await vscode.workspace.openTextDocument(parsedUri)

const range = new vscode.Range(
doc.positionAt(0),
doc.positionAt(doc.getText().length)
)

const wsEdit = new vscode.WorkspaceEdit()
wsEdit.replace(parsedUri, range, code)
vscode.workspace.applyEdit(wsEdit)
modifyFile(uri, modifiers) {
return applyModifiers(uri, modifiers)
}
})

Expand Down
32 changes: 30 additions & 2 deletions tests/parser/modifier.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {
insertBefore,
insertAfter,
remove,
replace
replace,
insertAt,
reduce
} from '@/parser/modifier'
import { transformTemplate } from '@/parser/template/transform'

describe('Modifier', () => {
describe('modify', () => {
it('should insert string before specified range', () => {
const code = 'const foo = "World";'
const actual = modify(code, [
Expand Down Expand Up @@ -101,3 +103,29 @@ describe('Modifier', () => {
expect(actual).toBe(expected)
})
})

describe('reduce', () => {
it('flattens and sorts the modifiers', () => {
const modifiers = [
insertAt(9, 'test'),
remove({ range: [0, 4] }),
replace({ range: [5, 10] }, 'foo')
]

const r = reduce(
modifiers,
(acc, m) => {
return {
pos: m.pos,
result: acc.result && acc.pos <= m.pos
}
},
{
pos: 0,
result: true
}
)

expect(r.result).toBe(true)
})
})

0 comments on commit 1eb1e94

Please sign in to comment.