Skip to content

Commit

Permalink
feat: add / remove declaration in preview (#11)
Browse files Browse the repository at this point in the history
* refactor: DeclarationUpdater -> DeclarationForUpdate

* chore: add `before` and `after` property in style node

* chore: add a helper to remove declaration

* chore: modify declaration to insert or remove

* feat: remove declaration from the code when its value is updated to empty in preview

* feat: add declaration when any area of rule element is clicked
  • Loading branch information
ktsn committed Mar 21, 2018
1 parent d0dbac0 commit decd3cb
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 53 deletions.
38 changes: 37 additions & 1 deletion src/message/bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getNode as getStyleNode } from '../parser/style/manipulate'
import { Modifiers, modify } from '../parser/modifier'
import { insertComponentScript } from '../parser/script/modify'
import { insertToTemplate } from '../parser/template/modify'
import { updateDeclaration } from '../parser/style/modify'
import { updateDeclaration, insertDeclaration, removeDeclaration } from '../parser/style/modify'
import { mapValues } from '../utils'

export function observeServerEvents(
Expand Down Expand Up @@ -135,6 +135,42 @@ export function observeServerEvents(
bus.emit('initProject', mapValues(vueFiles, vueFileToPayload))
})

bus.on('addDeclaration', ({ uri, path, declaration }) => {
const { code, styles } = vueFiles[uri]
const added = modify(code, [
insertDeclaration(styles, declaration, path)
])

bus.emit('updateEditor', {
uri,
code: added
})

// TODO: move mutation to outside of this logic
vueFiles[uri] = parseVueFile(added, uri)

// TODO: change this notification more clean and optimized way
bus.emit('initProject', mapValues(vueFiles, vueFileToPayload))
})

bus.on('removeDeclaration', ({ uri, path }) => {
const { code, styles } = vueFiles[uri]
const removed = modify(code, [
removeDeclaration(styles, path)
])

bus.emit('updateEditor', {
uri,
code: removed
})

// TODO: move mutation to outside of this logic
vueFiles[uri] = parseVueFile(removed, uri)

// TODO: change this notification more clean and optimized way
bus.emit('initProject', mapValues(vueFiles, vueFileToPayload))
})

bus.on('updateDeclaration', ({ uri, declaration }) => {
const { code, styles } = vueFiles[uri]
const updated = modify(code, [updateDeclaration(styles, declaration)])
Expand Down
13 changes: 11 additions & 2 deletions src/message/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DeclarationUpdater } from '../parser/style/types'
import { DeclarationForUpdate, DeclarationForAdd } from '../parser/style/types'
import { VueFilePayload } from '../parser/vue-file'

export interface Events {
Expand Down Expand Up @@ -27,9 +27,18 @@ export interface Events {
code: string
}
removeDocument: string
addDeclaration: {
uri: string
path: number[]
declaration: DeclarationForAdd
}
removeDeclaration: {
uri: string
path: number[]
}
updateDeclaration: {
uri: string
declaration: DeclarationUpdater
declaration: DeclarationForUpdate
}
}

Expand Down
10 changes: 7 additions & 3 deletions src/parser/modifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,18 @@ export function insertAfter(node: Range, value: string): Modifier {
return insertAt(node.range[1], value)
}

export function remove(node: Range): Modifier {
export function removeRange(from: number, to: number): Modifier {
return {
type: 'Remove',
pos: node.range[0],
length: node.range[1] - node.range[0]
pos: from,
length: to - from
}
}

export function remove(node: Range): Modifier {
return removeRange(node.range[0], node.range[1])
}

export function replace(node: Range, value: string): Modifier[] {
return [remove(node), insertAfter(node, value)]
}
2 changes: 1 addition & 1 deletion src/parser/style/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function genAtRule(atRule: t.AtRule): string {
return buf
}

function genRule(rule: t.Rule): string {
export function genRule(rule: t.Rule): string {
const selectors = rule.selectors.map(genSelector).join(', ')
const declarations = rule.declarations.map(genDeclaration).join(' ')

Expand Down
84 changes: 73 additions & 11 deletions src/parser/style/modify.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,80 @@
import { Style, DeclarationUpdater } from './types'
import { getDeclaration } from './manipulate'
import { Modifier, empty, replace } from '../modifier'
import { genDeclaration } from './codegen'
import {
Style,
DeclarationForUpdate,
DeclarationForAdd,
Declaration
} from './types'
import { getDeclaration, getNode } from './manipulate'
import {
Modifier,
empty,
replace,
removeRange,
insertBefore,
insertAfter
} from '../modifier'
import { genDeclaration, genRule } from './codegen'
import { clone } from '../../utils'

export function updateDeclaration(
export function insertDeclaration(
styles: Style[],
decl: DeclarationUpdater
): Modifier[] {
const target = getDeclaration(styles, decl.path)
decl: DeclarationForAdd,
to: number[]
): Modifier | Modifier[] {
const target = getDeclaration(styles, to)
if (target) {
return insertBefore(
target,
genDeclaration(clone(target, decl)) + target.before
)
}

const parentPath = to.slice(0, -1)
const last = to[to.length - 1]
const before = getDeclaration(styles, parentPath.concat(last - 1))
if (before) {
return insertAfter(
before,
before.before + genDeclaration(clone(before, decl))
)
}

const rule = getNode(styles, parentPath)
if (rule && rule.type === 'Rule') {
const d: Declaration = {
type: 'Declaration',
path: to,
before: '',
after: '',
range: [-1, -1],
...decl
}

const inserted = clone(rule, {
declarations: [
...rule.declarations.slice(last),
d,
...rule.declarations.slice(last + 1)
]
})

if (!target) {
return [empty]
return replace(rule, genRule(inserted))
}

return replace(target, genDeclaration(clone(target, decl)))
return empty
}

export function removeDeclaration(styles: Style[], path: number[]): Modifier {
const target = getDeclaration(styles, path)
return target
? removeRange(target.range[0] - target.before.length, target.range[1])
: empty
}

export function updateDeclaration(
styles: Style[],
decl: DeclarationForUpdate
): Modifier[] {
const target = getDeclaration(styles, decl.path)
return target ? replace(target, genDeclaration(clone(target, decl))) : [empty]
}
6 changes: 6 additions & 0 deletions src/parser/style/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ function transformAtRule(
return {
type: 'AtRule',
path,
before: atRule.raws.before || '',
after: atRule.raws.after || '',
name: atRule.name,
params: atRule.params,
children: children.map((child, i) => {
Expand All @@ -75,6 +77,8 @@ function transformRule(
return {
type: 'Rule',
path,
before: rule.raws.before || '',
after: rule.raws.after || '',
selectors: root.nodes.map(n => {
// A child of root node is always selector
const selectors = (n as selectorParser.Selector).nodes
Expand Down Expand Up @@ -200,6 +204,8 @@ function transformDeclaration(
return {
type: 'Declaration',
path,
before: decl.raws.before || '',
after: decl.raws.after || '',
prop: decl.prop,
value: decl.value,
important: decl.important || false, // decl.import is possibly undefined
Expand Down
14 changes: 13 additions & 1 deletion src/parser/style/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ export interface Style extends Range {

export interface Rule extends Range {
type: 'Rule'
before: string
after: string
path: number[]
selectors: Selector[]
declarations: Declaration[]
}

export interface Declaration extends Range {
type: 'Declaration'
before: string
after: string
path: number[]
prop: string
value: string
Expand All @@ -22,6 +26,8 @@ export interface Declaration extends Range {

export interface AtRule extends Range {
type: 'AtRule'
before: string
after: string
path: number[]
name: string
params: string
Expand Down Expand Up @@ -87,7 +93,13 @@ export interface DeclarationForPrint {
/**
* Used to update ast
*/
export interface DeclarationUpdater {
export interface DeclarationForAdd {
prop: string
value: string
important: boolean
}

export interface DeclarationForUpdate {
path: number[]
prop?: string
value?: string
Expand Down
24 changes: 21 additions & 3 deletions src/payload.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { VueFilePayload } from './parser/vue-file'
import { DeclarationUpdater } from './parser/style/types'
import { DeclarationForUpdate, DeclarationForAdd } from './parser/style/types'

export type ServerPayload = InitProject | ChangeDocument
export type ClientPayload = SelectNode | AddNode | UpdateDeclaration
export type ClientPayload =
| SelectNode
| AddNode
| AddDeclaration
| RemoveDeclaration
| UpdateDeclaration

export interface InitProject {
type: 'InitProject'
Expand All @@ -28,8 +33,21 @@ export interface AddNode {
path: number[]
}

export interface AddDeclaration {
type: 'AddDeclaration'
uri: string
path: number[]
declaration: DeclarationForAdd
}

export interface RemoveDeclaration {
type: 'RemoveDeclaration'
uri: string
path: number[]
}

export interface UpdateDeclaration {
type: 'UpdateDeclaration'
uri: string
declaration: DeclarationUpdater
declaration: DeclarationForUpdate
}
4 changes: 4 additions & 0 deletions src/server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export function wsEventObserver(
return emit('selectNode', payload)
case 'AddNode':
return emit('addNode', payload)
case 'AddDeclaration':
return emit('addDeclaration', payload)
case 'RemoveDeclaration':
return emit('removeDeclaration', payload)
case 'UpdateDeclaration':
return emit('updateDeclaration', payload)
default:
Expand Down
4 changes: 4 additions & 0 deletions src/view/components/PageMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
<StyleInformation
v-if="matchedRules.length > 0"
:rules="matchedRules"
@add-declaration="addDeclaration"
@remove-declaration="removeDeclaration"
@update-declaration="updateDeclaration"
/>
<p class="not-found" v-else>Not found</p>
Expand Down Expand Up @@ -99,6 +101,8 @@ export default Vue.extend({
'setDraggingPlace',
'select',
'applyDraggingElement',
'addDeclaration',
'removeDeclaration',
'updateDeclaration'
])
})
Expand Down

0 comments on commit decd3cb

Please sign in to comment.