diff --git a/packages/dev-server/comp/menu/index.js b/packages/dev-server/comp/menu/index.js
index 0424e19..a6e9153 100644
--- a/packages/dev-server/comp/menu/index.js
+++ b/packages/dev-server/comp/menu/index.js
@@ -14,16 +14,15 @@ for (const command of require('./command.json')) {
commands[key] = command
}
-const menus = require('./menus.json')
const menuData = {}
-const menuKeys = Object.keys(menus)
-for (const key of menuKeys) {
- menuData[key] = {
- show: false,
- content: menus[key],
- embed: new Array(menus[key].length).fill(false)
+require('./menus.json').forEach(function traverse(menu) {
+ if (menu.children) {
+ menuData[menu.key] = menu
+ menuData[menu.key].show = false
+ menuData[menu.key].current = null
}
-}
+})
+const menuKeys = Object.keys(menuData)
module.exports = {
components: {
@@ -57,9 +56,9 @@ module.exports = {
})
}
this.menuReference = {}
- for (let index = 0; index < menuKeys.length; index++) {
- this.menuReference[menuKeys[index]] = this.$refs.menus.$el.children[index]
- }
+ menuKeys.forEach((key, index) => {
+ this.menuReference[key] = this.$refs.menus.$el.children[index]
+ })
},
methods: {
@@ -92,9 +91,7 @@ module.exports = {
this.menubarActive = false
for (const key in this.menuData) {
this.menuData[key].show = false
- for (let index = 0; index < this.menuData[key].embed.length; index++) {
- this.menuData[key].embed.splice(index, 1, false)
- }
+ this.menuData[key].current = null
}
},
showContextMenu(key, event) {
@@ -118,7 +115,7 @@ module.exports = {
}
},
hoverMenu(index, event) {
- if (this.menubarActive && !this.menuData.menubar.embed[index]) {
+ if (this.menubarActive && this.menuData.menubar.current !== index) {
this.showMenu(index, event)
}
},
@@ -130,11 +127,11 @@ module.exports = {
},
showMenu(index, event) {
const style = this.menuReference.menubar.style
- const last = this.menuData.menubar.embed.indexOf(true)
+ const last = this.menuData.menubar.current
if (last === index) {
this.menubarActive = false
this.menuData.menubar.show = false
- this.menuData.menubar.embed.splice(index, 1, false)
+ this.menuData.menubar.current = null
return
} else if (last === -1) {
this.menubarMove = 0
@@ -145,7 +142,7 @@ module.exports = {
this.locateMenuAtButton(event, style)
this.menubarActive = true
this.menuData.menubar.show = true
- this.menuData.menubar.embed.splice(index, 1, true)
+ this.menuData.menubar.current = index
},
locateMenuAtButton(event, style) {
const rect = event.currentTarget.getBoundingClientRect()
diff --git a/packages/dev-server/comp/menu/menu-manager.vue b/packages/dev-server/comp/menu/menu-manager.vue
index 0d3c409..432e38a 100644
--- a/packages/dev-server/comp/menu/menu-manager.vue
+++ b/packages/dev-server/comp/menu/menu-manager.vue
@@ -14,7 +14,7 @@ module.exports = {
diff --git a/packages/dev-server/comp/menu/menu-view.vue b/packages/dev-server/comp/menu/menu-view.vue
index 5f87ae8..8056b45 100644
--- a/packages/dev-server/comp/menu/menu-view.vue
+++ b/packages/dev-server/comp/menu/menu-view.vue
@@ -3,15 +3,11 @@
module.exports = {
name: 'marklet-menu-view',
inject: ['commands', '$menu'],
- props: ['data', 'embed'],
+ props: ['data', 'current'],
components: {
MarkletMenuList: require('./menu-list.vue'),
MarkletMenuItem: require('./menu-item.vue'),
},
-
- created() {
- console.log(this.data)
- }
}
@@ -20,8 +16,8 @@ module.exports = {
-
-
+
+
diff --git a/packages/dev-server/comp/menu/menus.json b/packages/dev-server/comp/menu/menus.json
index 766412e..0ead432 100644
--- a/packages/dev-server/comp/menu/menus.json
+++ b/packages/dev-server/comp/menu/menus.json
@@ -1,53 +1,56 @@
-{
- "menubar": [
+[{
+ "key": "menubar",
+ "children": [
{
- "key": "file",
"caption": "文件",
"mnemonic": "F",
- "content": [
+ "children": [
{ "command": "open-file", "mnemonic": "O" },
"@separator",
{ "command": "save", "mnemonic": "S" },
- { "command": "save-as", "mnemonic": "" },
- { "command": "save-all", "mnemonic": "" }
+ { "command": "save-as", "mnemonic": "A" },
+ { "command": "save-all", "mnemonic": "L" }
]
},
{
- "key": "edit",
"caption": "编辑",
"mnemonic": "E",
- "content": [
- { "command": "undo", "mnemonic": "" },
- { "command": "redo", "mnemonic": "" },
+ "children": [
+ { "command": "undo", "mnemonic": "U" },
+ { "command": "redo", "mnemonic": "R" },
"@separator",
- { "command": "cut", "mnemonic": "" },
- { "command": "copy", "mnemonic": "" },
- { "command": "paste", "mnemonic": "" },
+ { "command": "cut", "mnemonic": "T" },
+ { "command": "copy", "mnemonic": "C" },
+ { "command": "paste", "mnemonic": "P" },
"@separator",
- { "command": "find", "mnemonic": "" },
- { "command": "find-next", "mnemonic": "" },
- { "command": "find-previous", "mnemonic": "" }
+ { "command": "find", "mnemonic": "F" },
+ { "command": "find-next" },
+ { "command": "find-previous" }
]
},
{
- "key": "view",
"caption": "视图",
"mnemonic": "V",
- "content": [
- { "command": "trigger-explorer", "mnemonic": "" },
- { "command": "trigger-editor", "mnemonic": "" },
- { "command": "trigger-document", "mnemonic": "" },
+ "children": [
+ { "command": "trigger-explorer", "mnemonic": "R" },
+ { "command": "trigger-editor", "mnemonic": "E" },
+ { "command": "trigger-document", "mnemonic": "D" },
"@separator",
- "@themes"
+ {
+ "caption": "主题",
+ "mnemonic": "T",
+ "children": [
+ "@themes"
+ ]
+ }
]
},
{
- "key": "link",
"caption": "链接",
"mnemonic": "L",
- "content": [
- { "command": "open-repository", "mnemonic": "" }
+ "children": [
+ { "command": "open-repository", "mnemonic": "R" }
]
}
]
-}
+}]
From d0c8a250efd143059a8cbc1ddd1ca006b05d835d Mon Sep 17 00:00:00 2001
From: Shigma <1700011071@pku.edu.cn>
Date: Wed, 24 Oct 2018 18:15:56 +0800
Subject: [PATCH 13/47] add some adjustments
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
尝试增加了对层叠式菜单的支持
---
packages/dev-server/comp/menu/command.json | 3 +-
packages/dev-server/comp/menu/index.js | 37 +++++++++++++++------
packages/dev-server/comp/menu/menu-item.vue | 27 ++++++++++++---
packages/dev-server/comp/menu/menu-view.vue | 24 ++++++++++++-
packages/dev-server/comp/menu/menus.json | 5 ++-
5 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/packages/dev-server/comp/menu/command.json b/packages/dev-server/comp/menu/command.json
index 951aafa..b047142 100644
--- a/packages/dev-server/comp/menu/command.json
+++ b/packages/dev-server/comp/menu/command.json
@@ -93,7 +93,8 @@
"arguments": "editor.action.quickCommand",
"key": "command-palette",
"bind": "!f1",
- "name": "命令面板"
+ "name": "命令面板",
+ "enabled": "$display.editor.show"
},
{
"method": "executeAction",
diff --git a/packages/dev-server/comp/menu/index.js b/packages/dev-server/comp/menu/index.js
index a6e9153..64c9e2a 100644
--- a/packages/dev-server/comp/menu/index.js
+++ b/packages/dev-server/comp/menu/index.js
@@ -9,17 +9,20 @@ function toKebab(camel) {
}
const commands = {}
-for (const command of require('./command.json')) {
+require('./command.json').forEach((command) => {
const key = command.key ? command.key : toKebab(command.method)
commands[key] = command
-}
+})
const menuData = {}
-require('./menus.json').forEach(function traverse(menu) {
+require('./menus.json').forEach(function walk(menu) {
+ if (menu.ref) {
+ menuData[menu.ref] = menu
+ menuData[menu.ref].show = false
+ menuData[menu.ref].current = null
+ }
if (menu.children) {
- menuData[menu.key] = menu
- menuData[menu.key].show = false
- menuData[menu.key].current = null
+ menu.children.forEach(walk)
}
})
const menuKeys = Object.keys(menuData)
@@ -67,6 +70,7 @@ module.exports = {
if (method instanceof Function) method(...args)
},
executeCommand(command) {
+ if (!command.method) return
const method = this[command.method]
if (!(method instanceof Function)) {
console.error(`No method ${command.method} was found!`)
@@ -101,7 +105,7 @@ module.exports = {
this.menuData[key].show = true
},
locateMenuAtClient(event, style) {
- if (event.clientX + 200 > this.width) {
+ if (event.clientX + 200 > innerWidth) {
style.left = event.clientX - 200 - this.left + 'px'
} else {
style.left = event.clientX - this.left + 'px'
@@ -122,7 +126,7 @@ module.exports = {
showButtonMenu(key, event) {
const style = this.menuReference[key].style
this.hideContextMenus()
- this.locateMenuAtButton(event, style)
+ this.locateAtTopBottom(event, style)
this.menuData[key].show = true
},
showMenu(index, event) {
@@ -139,19 +143,30 @@ module.exports = {
this.menubarMove = index - last
}
this.hideContextMenus()
- this.locateMenuAtButton(event, style)
+ this.locateAtTopBottom(event, style)
this.menubarActive = true
this.menuData.menubar.show = true
this.menuData.menubar.current = index
},
- locateMenuAtButton(event, style) {
+ locateAtTopBottom(event, style) {
const rect = event.currentTarget.getBoundingClientRect()
- if (rect.left + 200 > this.width) {
+ if (rect.left + 200 > innerWidth) {
style.left = rect.left + rect.width - 200 + 'px'
} else {
style.left = rect.left + 'px'
}
style.top = rect.top + rect.height + 'px'
},
+ locateAtLeftRight(event, style) {
+ const rect = event.currentTarget.getBoundingClientRect()
+ if (rect.right + 200 > innerWidth) {
+ style.left = null
+ style.right = rect.left + 'px'
+ } else {
+ style.right = null
+ style.left = rect.right + 'px'
+ }
+ style.top = rect.top + 'px'
+ },
},
}
diff --git a/packages/dev-server/comp/menu/menu-item.vue b/packages/dev-server/comp/menu/menu-item.vue
index 9751539..cda7c05 100644
--- a/packages/dev-server/comp/menu/menu-item.vue
+++ b/packages/dev-server/comp/menu/menu-item.vue
@@ -2,15 +2,32 @@
module.exports = {
inject: ['$menu'],
- props: ['command', 'mnemonic'],
+ props: {
+ command: {
+ type: Object,
+ default: {},
+ },
+ mnemonic: {
+ type: String,
+ default: '',
+ },
+ binding: {
+ type: String,
+ default: '',
+ },
+ caption: {
+ type: String,
+ default: '',
+ },
+ },
computed: {
disabled() {
if (!this.command.enabled) return
return !this.$menu.parseArgument(this.command.enabled)
},
- binding() {
- let binding = this.command.bind
+ keybinding() {
+ let binding = this.binding || this.command.bind
if (!binding) return ''
if (binding.charAt(0) === '!') binding = binding.slice(1)
return binding.replace(/[a-z]+/g, word => {
@@ -34,11 +51,11 @@ module.exports = {
- {{ command.name }}
+ {{ caption || command.name }}
({{ mnemonic }})
...
- {{ binding }}
+ {{ keybinding }}
diff --git a/packages/dev-server/comp/menu/menu-view.vue b/packages/dev-server/comp/menu/menu-view.vue
index 8056b45..82020c6 100644
--- a/packages/dev-server/comp/menu/menu-view.vue
+++ b/packages/dev-server/comp/menu/menu-view.vue
@@ -8,6 +8,22 @@ module.exports = {
MarkletMenuList: require('./menu-list.vue'),
MarkletMenuItem: require('./menu-item.vue'),
},
+
+ methods: {
+ enterMenuItem(key, event) {
+ const style = this.$menu.menuReference[key].style
+ this.$menu.locateAtLeftRight(event, style)
+ this.$menu.menuData[key].show = true
+ },
+ leaveMenuItem(key, event) {
+ const x = event.clientX
+ const y = event.clientY
+ const rect = this.$el.getBoundingClientRect()
+ if (x >= rect.left && x <= rect.right && y >= rect.left && y <= rect.right) {
+ this.$menu.menuData[key].show = false
+ }
+ },
+ },
}
@@ -15,7 +31,13 @@ module.exports = {
-
+
+
+
+
diff --git a/packages/dev-server/comp/menu/menus.json b/packages/dev-server/comp/menu/menus.json
index 0ead432..ab9da7b 100644
--- a/packages/dev-server/comp/menu/menus.json
+++ b/packages/dev-server/comp/menu/menus.json
@@ -1,5 +1,5 @@
[{
- "key": "menubar",
+ "ref": "menubar",
"children": [
{
"caption": "文件",
@@ -36,7 +36,10 @@
{ "command": "trigger-editor", "mnemonic": "E" },
{ "command": "trigger-document", "mnemonic": "D" },
"@separator",
+ { "command": "command-palette", "mnemonic": "C" },
+ "@separator",
{
+ "ref": "themes",
"caption": "主题",
"mnemonic": "T",
"children": [
From 9c418729fd3c8a36b5218ac8d571391a928a22c7 Mon Sep 17 00:00:00 2001
From: Shigma <1700011071@pku.edu.cn>
Date: Wed, 24 Oct 2018 18:22:25 +0800
Subject: [PATCH 14/47] cli options support filepath
---
packages/cli/program.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/cli/program.js b/packages/cli/program.js
index 0b8cdef..880876a 100644
--- a/packages/cli/program.js
+++ b/packages/cli/program.js
@@ -64,6 +64,7 @@ Object.assign(Object.getPrototypeOf(program), {
if (!MARK_TYPES.includes(path.extname(basePath))) {
options = loadFromFile(basePath)
}
+ options.filepath = basePath
} else {
let matchFound = false
basePath = path.join(basePath, 'marklet')
@@ -72,6 +73,7 @@ Object.assign(Object.getPrototypeOf(program), {
if (!fs.existsSync(filename)) continue
if (fs.statSync(filename).isFile()) {
options = loadFromFile(basePath)
+ options.filepath = basePath
matchFound = true
break
}
From 7b775efe991eae07ea25271a4eb7ad3d9f05f7b3 Mon Sep 17 00:00:00 2001
From: jjyyxx
Date: Wed, 24 Oct 2018 19:38:55 +0800
Subject: [PATCH 15/47] path fix
---
packages/cli/program.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/cli/program.js b/packages/cli/program.js
index 880876a..18bcb71 100644
--- a/packages/cli/program.js
+++ b/packages/cli/program.js
@@ -67,12 +67,12 @@ Object.assign(Object.getPrototypeOf(program), {
options.filepath = basePath
} else {
let matchFound = false
- basePath = path.join(basePath, 'marklet')
+ const configPathWithoutExt = path.join(basePath, 'marklet')
for (const type of ALL_TYPES) {
- const filename = basePath + type
- if (!fs.existsSync(filename)) continue
- if (fs.statSync(filename).isFile()) {
- options = loadFromFile(basePath)
+ const configPath = configPathWithoutExt + type
+ if (!fs.existsSync(configPath)) continue
+ if (fs.statSync(configPath).isFile()) {
+ options = loadFromFile(configPath)
options.filepath = basePath
matchFound = true
break
From 5b77714642c6b46a01404081dd95b9806df6a1ba Mon Sep 17 00:00:00 2001
From: jjyyxx
Date: Wed, 24 Oct 2018 19:42:05 +0800
Subject: [PATCH 16/47] Relatively comprehensive file watch mode Focus on impl
at present, code style issues (such as oop) shall be postponed.
---
package.json | 1 +
packages/dev-server/package.json | 1 +
packages/dev-server/src/server.ts | 109 +++++++++++++++++---------
packages/dev-server/src/serverUtil.ts | 13 +++
packages/test/data/marklet.json | 1 +
5 files changed, 86 insertions(+), 39 deletions(-)
create mode 100644 packages/dev-server/src/serverUtil.ts
create mode 100644 packages/test/data/marklet.json
diff --git a/package.json b/package.json
index 8050427..a9c05c1 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@sfc2js/sass": "^2.0.0",
"@types/cheerio": "^0.22.9",
"@types/js-yaml": "^3.11.2",
+ "@types/lodash.debounce": "^4.0.4",
"@types/node": "^10.12.0",
"@types/ws": "^6.0.1",
"ajv": "^6.5.4",
diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json
index 641c719..3074a2b 100644
--- a/packages/dev-server/package.json
+++ b/packages/dev-server/package.json
@@ -29,6 +29,7 @@
"dependencies": {
"@marklet/parser": "^1.5.2",
"@marklet/renderer": "^1.3.2",
+ "lodash.debounce": "^4.0.8",
"monaco-editor": "^0.14.3",
"mousetrap": "^1.6.2",
"vue": "^2.5.17",
diff --git a/packages/dev-server/src/server.ts b/packages/dev-server/src/server.ts
index 2701b36..632fa36 100644
--- a/packages/dev-server/src/server.ts
+++ b/packages/dev-server/src/server.ts
@@ -3,8 +3,10 @@ import * as http from 'http'
import * as url from 'url'
import * as fs from 'fs'
import ws from 'ws'
+import debounce from 'lodash.debounce'
import { LexerConfig } from '@marklet/parser'
+import { getContentType } from './serverUtil'
export const DEFAULT_PORT = 10826
@@ -41,6 +43,7 @@ class MarkletServer {
config: LexerConfig
wsServer: ws.Server
httpServer: http.Server
+ watcher: fs.FSWatcher
constructor(type: T, options: ServerOptions = {}) {
this.type = type
@@ -48,7 +51,13 @@ class MarkletServer {
this.config = options.config || {}
this.port = options.port || DEFAULT_PORT
this.sourceType = options.sourceType || 'file'
+ this.createServer()
+ if (this.filepath) {
+ this.setupContentWatcher()
+ }
+ }
+ private createServer() {
this.httpServer = http.createServer((request, response) => {
function handleError(error: Error) {
console.error(error)
@@ -73,8 +82,8 @@ class MarkletServer {
}
} else if (pathname === 'initialize.js') {
handleData(`
- marklet.type = '${type}'
- marklet.filepath = '${this.filepath}'
+ marklet.type = '${this.type}'
+ marklet.filepath = ${JSON.stringify(this.filepath)}
marklet.sourceType = '${this.sourceType}'
marklet.config = ${JSON.stringify(this.config)}
`)
@@ -88,52 +97,74 @@ class MarkletServer {
return
}
- const ext = path.extname(filepath)
- let contentType: string
- switch (ext) {
- case '.css': contentType = 'text/css'; break
- case '.js': contentType = 'text/javascript'; break
- case '.html': contentType = 'text/html'; break
- default: contentType = 'application/octet-stream'
- }
- handleData(data.toString(), contentType)
+ handleData(data.toString(), getContentType(filepath))
})
}).listen(this.port)
-
this.wsServer = new ws.Server({ server: this.httpServer })
console.log(`Server running at http://localhost:${this.port}/`)
+ }
- if (!this.filepath) return
+ private setupContentWatcher() {
if (this.sourceType === 'file') {
- this.wsServer.on('connection', (ws) => {
- ws.send(FileMessage(this.filepath))
- })
- fs.watch(this.filepath, (eventType: WatchEventType, filename) => {
- if (eventType === 'rename') {
- if (fs.existsSync(filename)) {
- this.filepath = filename // got new name
- } else {
- // TODO: gracefully exit since source file gone
- }
- } else {
- this.wsServer.broadcast(FileMessage(this.filepath))
- }
- })
+ this.setupFileWatcher()
} else {
- this.wsServer.on('connection', (ws) => {
- for (const message in ProjectMessages(this.filepath)) {
- ws.send(message)
- }
- })
- fs.watch(this.filepath, { recursive: true }, (eventType: WatchEventType, filename) => {
- // FIXME: only watching the index file is far from enough
- // you need to watch the basedir and every files in it
- for (const message in ProjectMessages(this.filepath)) {
- this.wsServer.broadcast(message)
- }
- })
+ this.setupProjectWatcher()
}
}
+
+ private setupFileWatcher() {
+ let content: string
+ let dirty: boolean
+ const updateContent = () => {
+ const temp = fs.readFileSync(this.filepath, 'utf8')
+ dirty = content !== temp
+ content = temp
+ }
+ updateContent()
+ const wrapDocumentMessage = () => JSON.stringify({
+ type: 'document',
+ filepath: this.filepath,
+ data: content,
+ })
+ this.wsServer.on('connection', (ws) => {
+ ws.send(wrapDocumentMessage())
+ })
+ const broadcast = debounce(() => {
+ updateContent()
+ if (dirty) {
+ this.wsServer.broadcast(wrapDocumentMessage())
+ }
+ }, 200)
+ this.watcher = fs.watch(this.filepath, (eventType: WatchEventType) => {
+ if (eventType === 'rename') {
+ this.dispose('Source file is gone.')
+ } else {
+ broadcast()
+ }
+ })
+ }
+
+ private setupProjectWatcher() {
+ this.wsServer.on('connection', (ws) => {
+ for (const message in ProjectMessages(this.filepath)) {
+ ws.send(message)
+ }
+ })
+ fs.watch(this.filepath, { recursive: true }, (eventType: WatchEventType, filename) => {
+ // FIXME: only watching the index file is far from enough
+ // you need to watch the basedir and every files in it
+ for (const message in ProjectMessages(this.filepath)) {
+ this.wsServer.broadcast(message)
+ }
+ })
+ }
+
+ public dispose(reason: string = '') {
+ this.wsServer.close()
+ this.httpServer.close()
+ this.watcher.close()
+ console.log(reason)
+ }
}
type FileTree = IterableIterator
diff --git a/packages/dev-server/src/serverUtil.ts b/packages/dev-server/src/serverUtil.ts
new file mode 100644
index 0000000..da7ea54
--- /dev/null
+++ b/packages/dev-server/src/serverUtil.ts
@@ -0,0 +1,13 @@
+import { extname } from 'path'
+
+const mimeMap: Record = {
+ '': 'application/octet-stream',
+ '.css': 'text/css',
+ '.js': 'text/javascript',
+ '.html': 'text/html'
+}
+
+export function getContentType(filepath: string): string {
+ const ext = extname(filepath)
+ return mimeMap[ext in mimeMap ? ext : '']
+}
\ No newline at end of file
diff --git a/packages/test/data/marklet.json b/packages/test/data/marklet.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/packages/test/data/marklet.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
From 28a83a4b8a531788e8c9afb6f7cd5f18a8202eb3 Mon Sep 17 00:00:00 2001
From: jjyyxx
Date: Wed, 24 Oct 2018 20:17:29 +0800
Subject: [PATCH 17/47] split code
---
packages/dev-server/src/ContentManager.ts | 44 +++++++++++++++++++++++
packages/dev-server/src/server.ts | 40 ++++-----------------
2 files changed, 51 insertions(+), 33 deletions(-)
create mode 100644 packages/dev-server/src/ContentManager.ts
diff --git a/packages/dev-server/src/ContentManager.ts b/packages/dev-server/src/ContentManager.ts
new file mode 100644
index 0000000..3bfc62c
--- /dev/null
+++ b/packages/dev-server/src/ContentManager.ts
@@ -0,0 +1,44 @@
+import { EventEmitter } from 'events'
+import { readFileSync, watch, FSWatcher } from 'fs'
+import debounce from 'lodash.debounce'
+
+type WatchEventType = 'rename' | 'change'
+
+export class FileManager extends EventEmitter {
+ private content: string
+ private dirty: boolean
+ private watcher: FSWatcher
+ private debouncedUpdate: () => void
+ public msg: string
+
+ constructor(private filepath: string) {
+ super()
+ this.update()
+ this.debouncedUpdate = debounce(() => this.update(), 200)
+ this.watcher = watch(this.filepath, (eventType: WatchEventType) => {
+ if (eventType === 'rename') {
+ this.dispose()
+ } else {
+ this.debouncedUpdate()
+ }
+ })
+ }
+
+ private update(): void {
+ const temp = readFileSync(this.filepath, 'utf8')
+ if (this.content !== temp) {
+ this.content = temp
+ this.msg = JSON.stringify({
+ type: 'document',
+ filepath: this.filepath,
+ data: this.content,
+ })
+ this.emit('update', this.msg)
+ }
+ }
+
+ public dispose(): void {
+ this.watcher.close()
+ this.emit('close')
+ }
+}
\ No newline at end of file
diff --git a/packages/dev-server/src/server.ts b/packages/dev-server/src/server.ts
index 632fa36..40dcb4c 100644
--- a/packages/dev-server/src/server.ts
+++ b/packages/dev-server/src/server.ts
@@ -3,10 +3,10 @@ import * as http from 'http'
import * as url from 'url'
import * as fs from 'fs'
import ws from 'ws'
-import debounce from 'lodash.debounce'
import { LexerConfig } from '@marklet/parser'
import { getContentType } from './serverUtil'
+import { FileManager } from './ContentManager'
export const DEFAULT_PORT = 10826
@@ -26,7 +26,6 @@ ws.Server.prototype.broadcast = function (data) {
export type ServerType = 'watch' | 'edit'
export type SourceType = 'file' | 'folder'
-export type WatchEventType = 'rename' | 'change'
interface ServerOptions {
port?: number
@@ -43,7 +42,6 @@ class MarkletServer {
config: LexerConfig
wsServer: ws.Server
httpServer: http.Server
- watcher: fs.FSWatcher
constructor(type: T, options: ServerOptions = {}) {
this.type = type
@@ -113,35 +111,12 @@ class MarkletServer {
}
private setupFileWatcher() {
- let content: string
- let dirty: boolean
- const updateContent = () => {
- const temp = fs.readFileSync(this.filepath, 'utf8')
- dirty = content !== temp
- content = temp
- }
- updateContent()
- const wrapDocumentMessage = () => JSON.stringify({
- type: 'document',
- filepath: this.filepath,
- data: content,
- })
- this.wsServer.on('connection', (ws) => {
- ws.send(wrapDocumentMessage())
- })
- const broadcast = debounce(() => {
- updateContent()
- if (dirty) {
- this.wsServer.broadcast(wrapDocumentMessage())
- }
- }, 200)
- this.watcher = fs.watch(this.filepath, (eventType: WatchEventType) => {
- if (eventType === 'rename') {
- this.dispose('Source file is gone.')
- } else {
- broadcast()
- }
+ const manager = new FileManager(this.filepath)
+ manager.once('close', () => {
+ this.dispose('Source file is gone.')
})
+ this.wsServer.on('connection', ws => ws.send(manager.msg))
+ manager.on('update', msg => this.wsServer.broadcast(msg))
}
private setupProjectWatcher() {
@@ -150,7 +125,7 @@ class MarkletServer {
ws.send(message)
}
})
- fs.watch(this.filepath, { recursive: true }, (eventType: WatchEventType, filename) => {
+ fs.watch(this.filepath, { recursive: true }, (eventType, filename) => {
// FIXME: only watching the index file is far from enough
// you need to watch the basedir and every files in it
for (const message in ProjectMessages(this.filepath)) {
@@ -162,7 +137,6 @@ class MarkletServer {
public dispose(reason: string = '') {
this.wsServer.close()
this.httpServer.close()
- this.watcher.close()
console.log(reason)
}
}
From f75523328f7e8cd4d5fe2aec241365a7033c3990 Mon Sep 17 00:00:00 2001
From: Shigma <1700011071@pku.edu.cn>
Date: Wed, 24 Oct 2018 20:27:57 +0800
Subject: [PATCH 18/47] menubar
---
packages/dev-server/comp/app.vue | 31 ++------
packages/dev-server/comp/menu/index.js | 29 +------
.../menu/{menu-manager.vue => manager.vue} | 0
packages/dev-server/comp/menu/menubar.vue | 79 +++++++++++++++++++
packages/dev-server/src/client.ts | 1 +
5 files changed, 86 insertions(+), 54 deletions(-)
rename packages/dev-server/comp/menu/{menu-manager.vue => manager.vue} (100%)
create mode 100644 packages/dev-server/comp/menu/menubar.vue
diff --git a/packages/dev-server/comp/app.vue b/packages/dev-server/comp/app.vue
index d57a4e5..ea4e1af 100644
--- a/packages/dev-server/comp/app.vue
+++ b/packages/dev-server/comp/app.vue
@@ -228,13 +228,7 @@ module.exports = {
-
+
@@ -250,6 +244,8 @@ module.exports = {
diff --git a/packages/dev-server/src/client.ts b/packages/dev-server/src/client.ts
index 8fa615f..7d5e743 100644
--- a/packages/dev-server/src/client.ts
+++ b/packages/dev-server/src/client.ts
@@ -10,6 +10,7 @@ declare global {
Vue.use(renderer)
Vue.component('mkl-checkbox', require('@/checkbox.vue'))
+Vue.component('ob-menubar', require('@/menu/menubar.vue'))
const eventBus = new Vue()
From c3b1038d77c00d780ec15a81196c0fa0b78e3cd4 Mon Sep 17 00:00:00 2001
From: Shigma <1700011071@pku.edu.cn>
Date: Wed, 24 Oct 2018 22:55:31 +0800
Subject: [PATCH 19/47] almost done
---
packages/dev-server/comp/.eslintrc.yml | 1 +
packages/dev-server/comp/app.vue | 23 +--
packages/dev-server/comp/menu/index.js | 179 +++++++-------------
packages/dev-server/comp/menu/manager.vue | 96 ++++++++++-
packages/dev-server/comp/menu/menu-list.vue | 6 +-
packages/dev-server/comp/menu/menu-view.vue | 5 +-
packages/dev-server/comp/menu/menubar.vue | 22 ++-
packages/dev-server/comp/menu/menus.json | 7 +-
packages/dev-server/src/client.ts | 2 +-
9 files changed, 186 insertions(+), 155 deletions(-)
diff --git a/packages/dev-server/comp/.eslintrc.yml b/packages/dev-server/comp/.eslintrc.yml
index 21af863..147b3ca 100644
--- a/packages/dev-server/comp/.eslintrc.yml
+++ b/packages/dev-server/comp/.eslintrc.yml
@@ -3,3 +3,4 @@ extends:
globals:
marklet: true
+
\ No newline at end of file
diff --git a/packages/dev-server/comp/app.vue b/packages/dev-server/comp/app.vue
index ea4e1af..c150aef 100644
--- a/packages/dev-server/comp/app.vue
+++ b/packages/dev-server/comp/app.vue
@@ -1,16 +1,18 @@
-
+
-
diff --git a/packages/dev-server/comp/menu/index.js b/packages/dev-server/comp/menu/index.js
index b95546a..ea3b4fa 100644
--- a/packages/dev-server/comp/menu/index.js
+++ b/packages/dev-server/comp/menu/index.js
@@ -1,4 +1,6 @@
const Mousetrap = require('mousetrap')
+const manager = require('./manager.vue')
+const menubar = require('./menubar.vue')
// TODO: improve this pattern maybe.
// Cause: firing event inside textarea is blocked by mousetrap by default.
@@ -8,138 +10,71 @@ function toKebab(camel) {
return camel.replace(/[A-Z]/g, char => '-' + char.toLowerCase())
}
-const commands = {}
-require('./command.json').forEach((command) => {
- const key = command.key ? command.key : toKebab(command.method)
- commands[key] = command
-})
+module.exports = function(Vue) {
+ Vue.component('ob-menubar', menubar)
-const menuData = {}
-require('./menus.json').forEach(function walk(menu) {
- if (menu.ref) {
- menuData[menu.ref] = menu
- menuData[menu.ref].show = false
- menuData[menu.ref].current = null
- }
- if (menu.children) {
- menu.children.forEach(walk)
- }
-})
-const menuKeys = Object.keys(menuData)
+ let $menu = null, hooks = []
+ const commandData = {}, menuData = {}
+ const MenuManager = Vue.extend(manager)
-module.exports = {
- components: {
- MarkletMenu: require('./manager.vue'),
- },
+ Object.defineProperty(Vue.prototype, '$menu', {
+ get: () => $menu
+ })
- data() {
- return {
- menuData,
- altKey: false,
+ Vue.prototype.onMenusLoad = function(callback) {
+ if ($menu) {
+ callback()
+ } else {
+ hooks.push(callback)
}
- },
-
- provide() {
- return {
- menuKeys,
- commands,
- $menu: this,
- }
- },
+ }
- mounted() {
- for (const key in commands) {
- const command = commands[key]
- if (!command.bind || command.bind.startsWith('!')) continue
+ Vue.prototype.registerCommands = function(commands) {
+ commands.forEach((command) => {
+ const key = command.key ? command.key : toKebab(command.method)
+ commandData[key] = command
+ if (!command.bind || command.bind.startsWith('!')) return
Mousetrap.bind(command.bind, () => {
- this.executeCommand(command)
+ if (!$menu) return
+ $menu.executeCommand(command)
return false
})
- }
- this.menuReference = {}
- menuKeys.forEach((key, index) => {
- this.menuReference[key] = this.$refs.menus.$el.children[index]
})
- },
+ }
- methods: {
- executeMethod(key, ...args) {
- const method = this[key]
- if (method instanceof Function) method(...args)
- },
- executeCommand(command) {
- if (!command.method) return
- const method = this[command.method]
- if (!(method instanceof Function)) {
- console.error(`No method ${command.method} was found!`)
- return
- }
- let args = command.arguments
- if (args === undefined) args = []
- if (!(args instanceof Array)) args = [args]
- method(...args.map(arg => this.parseArgument(arg)))
- },
- parseArgument(arg) {
- if (typeof arg !== 'string') return arg
- if (arg.startsWith('!$')) {
- return !arg.slice(2).split('.').reduce((prev, curr) => prev[curr], this)
- } else if (arg.startsWith('$')) {
- return arg.slice(1).split('.').reduce((prev, curr) => prev[curr], this)
- } else {
- return arg
- }
- },
- hideContextMenus() {
- for (const key in this.menuData) {
- this.menuData[key].show = false
- this.menuData[key].current = null
- }
- },
- showContextMenu(key, event) {
- const style = this.menuReference[key].style
- this.hideContextMenus()
- this.locateMenuAtClient(event, style)
- this.menuData[key].show = true
- },
- locateMenuAtClient(event, style) {
- if (event.clientX + 200 > innerWidth) {
- style.left = event.clientX - 200 - this.left + 'px'
- } else {
- style.left = event.clientX - this.left + 'px'
+ Vue.prototype.registerMenus = function(menus) {
+ menus.forEach(function walk(menu) {
+ if (menu.ref) {
+ menuData[menu.ref] = menu
+ menuData[menu.ref].show = false
+ menuData[menu.ref].current = null
}
- if (event.clientY - this.top > this.height / 2) {
- style.top = ''
- style.bottom = this.top + this.height - event.clientY + 'px'
- } else {
- style.top = event.clientY - this.top + 'px'
- style.bottom = ''
+ if (menu.children) {
+ menu.children.forEach(walk)
}
- },
- showButtonMenu(key, event) {
- const style = this.menuReference[key].style
- this.hideContextMenus()
- this.locateAtTopBottom(event, style)
- this.menuData[key].show = true
- },
- locateAtTopBottom(event, style) {
- const rect = event.currentTarget.getBoundingClientRect()
- if (rect.left + 200 > innerWidth) {
- style.left = rect.left + rect.width - 200 + 'px'
- } else {
- style.left = rect.left + 'px'
- }
- style.top = rect.top + rect.height + 'px'
- },
- locateAtLeftRight(event, style) {
- const rect = event.currentTarget.getBoundingClientRect()
- if (rect.right + 200 > innerWidth) {
- style.left = null
- style.right = rect.left + 'px'
- } else {
- style.right = null
- style.left = rect.right + 'px'
- }
- style.top = rect.top + 'px'
- },
- },
+ })
+ const menuKeys = Object.keys(menuData)
+ const element = this.$el.appendChild(document.createElement('div'))
+ $menu = new MenuManager({
+ propsData: { menuData, menuKeys },
+ provide() {
+ return {
+ commands: commandData,
+ $menu: this,
+ }
+ },
+ })
+ $menu.$context = this
+ $menu.$mount(element)
+
+ hooks.forEach(callback => callback())
+
+ this.$el.addEventListener('click', () => {
+ $menu.hideContextMenus()
+ })
+
+ this.$el.addEventListener('contextmenu', () => {
+ $menu.hideContextMenus()
+ })
+ }
}
diff --git a/packages/dev-server/comp/menu/manager.vue b/packages/dev-server/comp/menu/manager.vue
index 432e38a..79e21d7 100644
--- a/packages/dev-server/comp/menu/manager.vue
+++ b/packages/dev-server/comp/menu/manager.vue
@@ -1,11 +1,99 @@
@@ -13,8 +101,8 @@ module.exports = {
diff --git a/packages/dev-server/comp/menu/menu-list.vue b/packages/dev-server/comp/menu/menu-list.vue
index 2abfeff..84fa4ea 100644
--- a/packages/dev-server/comp/menu/menu-list.vue
+++ b/packages/dev-server/comp/menu/menu-list.vue
@@ -9,10 +9,10 @@ module.exports = {
-
- {{ list.prefix }}{{ item.name }}{{ list.postfix }}
+ {{ item.name }}
diff --git a/packages/dev-server/comp/menu/menu-view.vue b/packages/dev-server/comp/menu/menu-view.vue
index 82020c6..6fd41ac 100644
--- a/packages/dev-server/comp/menu/menu-view.vue
+++ b/packages/dev-server/comp/menu/menu-view.vue
@@ -33,7 +33,7 @@ module.exports = {
-
@@ -41,8 +41,9 @@ module.exports = {
-
+