- Node进行文件数据处理
- Vue.js进行视图的呈现
- Electron作为客户端
按照代码行进行读取, 每次读取到一行就进行遍历分析
/**
* 关键字
*/
let keyWords = [
'asm', 'auto',
'bool', 'break',
'case', 'catch', 'char', 'class', 'const', 'continue', 'cin', 'cout',
'default', 'delete', 'do', 'double', 'define',
'else', 'enum', 'except', 'explicit', 'extern', 'endl',
'false', 'finally', 'float', 'for', 'friend',
'goto',
'if', 'inline', 'int', 'include',
'long',
'mutable', 'main',
'namespace', 'new',
'operator',
'private', 'protectde', 'public', 'printf',
'register', 'return',
'short', 'signed', 'szieof', 'static', 'struct', 'string', 'switch', 'std', 'scanf',
'template', 'this', 'throw', 'true', 'try', 'typedef', 'typename',
'union', 'unsigned', 'using',
'virtual', 'void',
'while'
]
/**
* 特殊字符
*/
let specialWords = [
',', ';', '(', ')', '{', '}', '#', '^', '?', ':', '.', '[', ']', '+', '-', '*', '/', '%', '=', '>', '<', '!', '~', '|', '&',
'&&', '||', '==', '>=', '<=', '!=', '++', '--', '::', '<<', '>>', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '->'
]
keyWords.forEach(word => {
wordsMap.set(word, 'keyWord')
})
specialWords.forEach(word => {
wordsMap.set(word, 'specialWord')
})
-
代码注释匹配
- 当读到一行中包含/*的两个字符时候, 这时把代码注释的标志设置为true, 然后一直读取,知道遇到*/的时候就把标志重新置为false
- 判断是否可以构成单词,成立的条件是不以数字开头,并且只包含数字, 下划线, 以及字母, 可以通过正则来/[a-z]|[A-z]|_/匹配
- 判断是否为字符串或者字符, 成立条件是以",'开头
- 判断是否为数字
- 判断是否为特殊字符, 这时就通过建立的映射进行关键字查找
- 判断空格
-
代码解释
判断工具函数
//判断是否是字母与下划线
function judgeWord(word) {
let wordPatten = /[a-z]|[A-z]|\_/
return wordPatten.test(word)
}
//判断是否为数字
function judgeNumber(number) {
let numberPatten = /[0-9]/
return numberPatten.test(number)
}
//判断是否为特殊字符
function judgeSpecialWord(letter) {
return wordsMap.get(letter) === 'specialWord' ? true : false
}
//判断是否为关键词
function judgeKeyWord(letter) {
return wordsMap.get(letter) === 'keyWord' ? true : false
}
行分析函数
exports.analysisLine = (line, annotation) => {
let oneLine = []
let word = ''
let i = 0
let includeFile = false
while (i < line.length) {
//注释代码的优先级最高,需要先进行判断
if (line[i] === '/' && line[i + 1] === '*') {
word += line[i]
i++
annotation = true;
}
if (!annotation) {
//表示不是注释内容
if (judgeWord(line[i])) {
//表示是属于字母与下划线
word += line[i]
while (i + 1 < line.length && (judgeNumber(line[i + 1]) || judgeWord(line[i + 1]))) {
i++
word += line[i]
}
if (judgeKeyWord(word)) {
//判断单词是否属于关键词
oneLine.push({
word: word,
type: 'keyWord'
})
} else {
//不为关键词, 则为标识符
oneLine.push({
word: word,
type: 'identifier'
})
}
//由于include是属于头文件的引入, 需要对头文件进行特殊处理
if (word === 'include') {
includeFile = true
}
word = ''
i++
} else if (line[i] === '"') {
//字符串判断
while (i + 1 < line.length && line[i + 1] !== '"') {
word += line[i]
i++
}
word += (line[i] || '' )+ (line[i + 1] || '')
oneLine.push({
word: word,
type: 'string'
})
word = ''
i += 2
} else if (line[i] === '\'') {
//字符判断
while (i + 1 < line.length && line[i + 1] !== '\'') {
word += line[i]
i++
}
word += (line[i] || '' )+ (line[i + 1] || '')
oneLine.push({
word: word,
type: 'char'
})
word = ''
i += 2
} else if (judgeNumber(line[i])) {
//数字判断
word += line[i]
while (i + 1 < line.length && (judgeNumber(line[i + 1]) || line[i + 1] === '.')) {
i++
word += line[i]
}
oneLine.push({
word: word,
type: 'number'
})
word = ''
i++
} else if (judgeSpecialWord(line[i])) {
//特殊字符判断
if (line[i] === '<' && includeFile) {
//处理头文件的引入
oneLine.push({
word: line[i],
type: 'specialWord'
})
i++
word += line[i]
while (i + 1 < line.length && line[i + 1] !== '>') {
i++
word += line[i]
}
oneLine.push({
word: word,
type: 'libraryFile'
})
word = ''
i++
} else {
//处理//的注释代码
if (line[i] === '/' && line[i + 1] === '/') {
i++
while (i + 1 < line.length) {
i++
word += line[i]
}
oneLine.push({
word: word,
type: 'Annotations'
})
word = ''
i += 3
} else {
word += line[i]
while (i + 1 < line.length && (judgeSpecialWord(word + line[i + 1]))) {
i++
word += line[i]
}
oneLine.push({
word: word,
type: 'specialWord'
})
word = ''
i++
}
}
} else if (line[i] === ' ') {
oneLine.push({
word: line[i],
type: 'space'
})
i++
}
} else {
//表示注释内容
while (i + 1 < line.length && (line[i + 1] !== '*' && line[i + 2] !== '/')) {
word += line[i]
i++
}
word += line[i] + (line[i + 1] || '') + (line[i + 2] || '')
oneLine.push({
word: word,
type: 'Annotations'
})
annotation = false;
word = ''
i += 3
}
}
return oneLine
}
- Electron, Vue搭建
具体的搭建过程省略, 可查看源码或自行查找资料
-
动态编辑显示
- 数据绑定, 使用v-model结合watch进行数据监控, 当数据发生变化的时候重新分析每行数据
- 二维数组保存分析的数据, 使用二维数组保存分析完的词法, 保留原生的代码行
- 通过v-bind:class对不同类型的词法进行代码高亮的简单呈现
<template>
<div class="container">
<div class="navigator">
<button class="menu-btn" @click="open">open</button>
<button class="menu-btn" @click="save">save</button>
</div>
<div class="content">
<div class="code-input">
<textarea v-model="codeInput">
</textarea>
</div>
<div class="code-show">
<div v-for="(line, index) in codeShowArr">
<p v-for="word in line" :class="word.type">
<template v-if="word.type==='space'">
 
</template>
<template v-else>
{{word.word}}
</template>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
import { analysisLine } from '../controllers/analysis'
import fileController from '../controllers/fileController'
export default {
watch: {
codeInput: function() {
this.codeShowArr = []
this.codeInput.split(/\r?\n/).forEach(line => {
let wordArr = []
analysisLine(line, this.annotation).forEach(word => {
wordArr.push(word)
})
this.codeShowArr.push(wordArr)
})
}
},
data () {
return {
codeInput: '',
codeShow: '',
codeShowArr: [],
annotation: false
}
},
methods: {
open() {
fileController.openDialog().then(filePath => {
return fileController.readPackage(filePath)
}).then(res => {
this.codeInput = res
})
},
save() {
fileController.openDialog().then(filePath => {
return fileController.writePackage(filePath, this.codeShowArr)
})
}
}
}
</script>
-
文件的读取和写入
- 使用Electron中引入Node的fs模块来进行文件读取写入处理
- 使用Electron的Dialog控件选取文件
import Promise from 'bluebird'
import electron from 'electron'
const {dialog} = electron.remote
const fs = Promise.promisifyAll(require('fs'));
const writePackage = (filePath, data) => {
return fs.writeFileAsync(filePath, JSON.stringify(data, null, 2)).catch(err => {
return Promise.reject(err)
});
}
const readPackage = (filePath) => {
return fs.readFileAsync(filePath, 'utf-8').catch(err => {
return Promise.reject(err)
});
}
const openDialog = () => {
return new Promise((resolve, reject) => {
dialog.showOpenDialog({
properties: [
'openFile',
]
}, (res) => {
if(!res) {
return reject('404')
}
return resolve(res[0])
});
})
}
export default {
writePackage,
readPackage,
openDialog
}