311 lines
12 KiB
TypeScript
311 lines
12 KiB
TypeScript
import * as path from 'path'
|
|
import * as vscode from 'vscode'
|
|
import { initialization } from './initialization'
|
|
import * as command from '@command/index'
|
|
import { DevPackages } from './views/DevPackages'
|
|
import { Devices, OpenocdService, SerialPortService } from '@service/index'
|
|
|
|
import * as fs from 'fs'
|
|
import * as net from 'net'
|
|
import * as os from 'os'
|
|
import { CancellationToken, DebugConfiguration, ProviderResult, WorkspaceFolder } from 'vscode'
|
|
import { IMyLogger } from '@utils/baseLogger'
|
|
import { BackendLogReceiver } from '@debug/frontend/lib/backendLogReceiver'
|
|
import { disposeChannel, FrontendChannelLogger } from '@utils/extensionLogger'
|
|
|
|
|
|
export const activate = async (context: vscode.ExtensionContext) => {
|
|
// Initialzate
|
|
await initialization(context)
|
|
|
|
// Create dependencies tree view
|
|
const dependenciesProvider = new DevPackages(vscode.workspace.rootPath)
|
|
vscode.window.registerTreeDataProvider('packageDependencies', dependenciesProvider)
|
|
vscode.commands.registerCommand('packageDependencies.refresh', () => dependenciesProvider.refresh())
|
|
|
|
// Create log channel
|
|
const openocdLogger = new FrontendChannelLogger('Openocd', 'openocd')
|
|
const uploadLogger = new FrontendChannelLogger('Upload', 'upload')
|
|
const serialportLogger = new FrontendChannelLogger('Serialport', 'serialport')
|
|
|
|
// Status bar items
|
|
const home = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100)
|
|
home.command = 'packageDependencies.createWebview'
|
|
home.text = '$(home) Kendryte Homepage'
|
|
home.show()
|
|
context.subscriptions.push(home)
|
|
|
|
const build = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
|
build.command = 'extension.build'
|
|
build.tooltip = 'Build'
|
|
build.text = '$(tools)'
|
|
build.show()
|
|
context.subscriptions.push(build)
|
|
|
|
const upload = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
|
upload.command = 'extension.buildAndUpload'
|
|
upload.tooltip = 'Build and Upload'
|
|
upload.text = '$(cloud-download)'
|
|
upload.show()
|
|
context.subscriptions.push(upload)
|
|
|
|
const debug = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
|
debug.command = 'extension.debug'
|
|
debug.tooltip = 'Debug'
|
|
debug.text = '$(bug)'
|
|
debug.show()
|
|
context.subscriptions.push(debug)
|
|
|
|
const serialport = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
|
serialport.command = 'extension.openSerialPort'
|
|
serialport.tooltip = 'Open serial port output'
|
|
serialport.text = '$(terminal)'
|
|
serialport.show()
|
|
context.subscriptions.push(serialport)
|
|
const updateSerialPort = (open: boolean) => {
|
|
serialport.text = !open ? '$(terminal)' : '$(primitive-square)'
|
|
serialport.tooltip = !open ? 'Open serial port output' : 'Stop serial port'
|
|
}
|
|
|
|
// SerialPort container service
|
|
const serialPortService = new SerialPortService(updateSerialPort, context.extensionPath)
|
|
|
|
// Devices check service
|
|
const deviceServices = new Devices(serialPortService)
|
|
deviceServices.on('setdevice', device => {
|
|
updateDevice(device)
|
|
})
|
|
|
|
const device = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
|
device.command = 'extension.pickDevice'
|
|
device.tooltip = 'Pick a device'
|
|
device.text = deviceServices.device || 'No device picked.'
|
|
device.show()
|
|
context.subscriptions.push(device)
|
|
const updateDevice = (deviceName: string | undefined) => {
|
|
device.text = deviceName || 'No device picked.'
|
|
}
|
|
|
|
// Register extension command
|
|
context.subscriptions.push(command.reinstallPackages(context))
|
|
context.subscriptions.push(command.build(context))
|
|
context.subscriptions.push(command.createHelloworld(context))
|
|
context.subscriptions.push(command.buildAndUpload(context, deviceServices, uploadLogger, serialPortService));
|
|
context.subscriptions.push(command.debug(context))
|
|
context.subscriptions.push(command.pickDevice(deviceServices))
|
|
context.subscriptions.push(command.configGenerate())
|
|
context.subscriptions.push(command.cmakelistGenerate())
|
|
context.subscriptions.push(command.createWebview(context))
|
|
context.subscriptions.push(command.dependenciesDownload(context))
|
|
context.subscriptions.push(command.addDependency(context, dependenciesProvider))
|
|
context.subscriptions.push(command.deleteDependency(dependenciesProvider))
|
|
context.subscriptions.push(command.openSerialPort(deviceServices, serialportLogger, serialPortService))
|
|
|
|
// Openocd service command
|
|
const openocdService = new OpenocdService(openocdLogger)
|
|
context.subscriptions.push(command.openocdStart(openocdService))
|
|
context.subscriptions.push(command.openocdStop(openocdService))
|
|
context.subscriptions.push(command.openocdRestart(openocdService))
|
|
|
|
// Debug Part
|
|
// The following part is copied from https://github.com/GongT/kendryte-ide-shell/blob/master/extensions.kendryte/kendryte-debug/src/frontend/extension.ts
|
|
context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('debugmemory', new MemoryContentProvider()))
|
|
context.subscriptions.push(vscode.commands.registerCommand('kendryte-debug.examineMemoryLocation', examineMemory))
|
|
context.subscriptions.push(vscode.commands.registerCommand('kendryte-debug.getFileNameNoExt', () => {
|
|
if (!vscode.window.activeTextEditor || !vscode.window.activeTextEditor.document || !vscode.window.activeTextEditor.document.fileName) {
|
|
vscode.window.showErrorMessage('No editor with valid file name active')
|
|
return
|
|
}
|
|
const fileName = vscode.window.activeTextEditor.document.fileName
|
|
const ext = path.extname(fileName)
|
|
return fileName.substr(0, fileName.length - ext.length)
|
|
}))
|
|
context.subscriptions.push(vscode.commands.registerCommand('kendryte-debug.getFileBasenameNoExt', () => {
|
|
if (!vscode.window.activeTextEditor || !vscode.window.activeTextEditor.document || !vscode.window.activeTextEditor.document.fileName) {
|
|
vscode.window.showErrorMessage('No editor with valid file name active')
|
|
return
|
|
}
|
|
const fileName = path.basename(vscode.window.activeTextEditor.document.fileName)
|
|
const ext = path.extname(fileName)
|
|
return fileName.substr(0, fileName.length - ext.length)
|
|
}))
|
|
context.subscriptions.push(new BackendLogReceiver())
|
|
context.subscriptions.push({
|
|
dispose: disposeChannel,
|
|
})
|
|
|
|
vscode.debug.registerDebugConfigurationProvider('kendryte', new Provider())
|
|
|
|
// Create config files and watch kendryte-package.json
|
|
const files = await vscode.workspace.findFiles('kendryte-package.json')
|
|
if (files.length > 0) {
|
|
await vscode.commands.executeCommand('extension.configGenerate')
|
|
const watcher = vscode.workspace.createFileSystemWatcher(files[0].path)
|
|
watcher.onDidChange(_ => {
|
|
vscode.commands.executeCommand('extension.configGenerate')
|
|
})
|
|
}
|
|
vscode.commands.executeCommand('packageDependencies.createWebview')
|
|
}
|
|
|
|
class Provider implements vscode.DebugConfigurationProvider {
|
|
private readonly logger: IMyLogger
|
|
|
|
constructor() {
|
|
this.logger = new FrontendChannelLogger('Provider', 'kendryte.gdb')
|
|
}
|
|
|
|
provideDebugConfigurations(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult<DebugConfiguration[]> {
|
|
this.logger.info('createDebugAdapterDescriptor', arguments)
|
|
return []
|
|
}
|
|
|
|
createDebugAdapterDescriptor() {
|
|
this.logger.info('createDebugAdapterDescriptor', arguments)
|
|
debugger
|
|
}
|
|
}
|
|
|
|
const memoryLocationRegex = /^0x[0-9a-f]+$/
|
|
|
|
function getMemoryRange(range: string) {
|
|
if (!range) {
|
|
return undefined
|
|
}
|
|
range = range.replace(/\s+/g, '').toLowerCase()
|
|
let index
|
|
if ((index = range.indexOf('+')) !== -1) {
|
|
const from = range.substr(0, index)
|
|
let length = range.substr(index + 1)
|
|
if (!memoryLocationRegex.exec(from)) {
|
|
return undefined
|
|
}
|
|
if (memoryLocationRegex.exec(length)) {
|
|
length = parseInt(length.substr(2), 16).toString()
|
|
}
|
|
return 'from=' + encodeURIComponent(from) + '&length=' + encodeURIComponent(length)
|
|
} else if ((index = range.indexOf('-')) != -1) {
|
|
const from = range.substr(0, index)
|
|
const to = range.substr(index + 1)
|
|
if (!memoryLocationRegex.exec(from)) {
|
|
return undefined
|
|
}
|
|
if (!memoryLocationRegex.exec(to)) {
|
|
return undefined
|
|
}
|
|
return 'from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to)
|
|
} else if (memoryLocationRegex.exec(range)) {
|
|
return 'at=' + encodeURIComponent(range)
|
|
} else {
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
function examineMemory() {
|
|
const socketlists = path.join(os.tmpdir(), 'kendryte-debug-sockets')
|
|
if (!fs.existsSync(socketlists)) {
|
|
if (process.platform == 'win32') {
|
|
return vscode.window.showErrorMessage('This command is not available on windows')
|
|
} else {
|
|
return vscode.window.showErrorMessage('No debugging sessions available')
|
|
}
|
|
}
|
|
fs.readdir(socketlists, (err, files) => {
|
|
if (err) {
|
|
if (process.platform == 'win32') {
|
|
return vscode.window.showErrorMessage('This command is not available on windows')
|
|
} else {
|
|
return vscode.window.showErrorMessage('No debugging sessions available')
|
|
}
|
|
}
|
|
const pickedFile = (file: string | undefined) => {
|
|
vscode.window.showInputBox({
|
|
placeHolder: 'Memory Location or Range',
|
|
validateInput: (range: string) => getMemoryRange(range) === undefined ? 'Range must either be in format 0xF00-0xF01, 0xF100+32 or 0xABC154' : '',
|
|
}).then(range => {
|
|
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.parse('debugmemory://' + file + '#' + getMemoryRange(range || '')))
|
|
})
|
|
}
|
|
if (files.length == 1) {
|
|
pickedFile(files[0])
|
|
} else if (files.length > 0) {
|
|
vscode.window.showQuickPick(files, { placeHolder: 'Running debugging instance' }).then(file => pickedFile(file))
|
|
} else if (process.platform == 'win32') {
|
|
return vscode.window.showErrorMessage('This command is not available on windows')
|
|
} else {
|
|
vscode.window.showErrorMessage('No debugging sessions available')
|
|
}
|
|
})
|
|
}
|
|
|
|
class MemoryContentProvider implements vscode.TextDocumentContentProvider {
|
|
provideTextDocumentContent(uri: vscode.Uri, token: vscode.CancellationToken): Thenable<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const conn = net.connect(path.join(os.tmpdir(), 'kendryte-debug-sockets', uri.authority))
|
|
let from: number, to: number
|
|
let highlightAt = -1
|
|
const splits = uri.fragment.split('&')
|
|
if (splits[0].split('=')[0] == 'at') {
|
|
const loc = parseInt(splits[0].split('=')[1].substr(2), 16)
|
|
highlightAt = 64
|
|
from = Math.max(loc - 64, 0)
|
|
to = Math.max(loc + 768, 0)
|
|
} else if (splits[0].split('=')[0] == 'from') {
|
|
from = parseInt(splits[0].split('=')[1].substr(2), 16)
|
|
if (splits[1].split('=')[0] == 'to') {
|
|
to = parseInt(splits[1].split('=')[1].substr(2), 16)
|
|
} else if (splits[1].split('=')[0] == 'length') {
|
|
to = from + parseInt(splits[1].split('=')[1])
|
|
} else {
|
|
return reject('Invalid Range')
|
|
}
|
|
} else {
|
|
return reject('Invalid Range')
|
|
}
|
|
if (to < from) {
|
|
return reject('Negative Range')
|
|
}
|
|
conn.write('examineMemory ' + JSON.stringify([from, to - from + 1]))
|
|
conn.once('data', data => {
|
|
let formattedCode = ''
|
|
const hexString = data.toString()
|
|
let x = 0
|
|
let asciiLine = ''
|
|
let byteNo = 0
|
|
for (let i = 0; i < hexString.length; i += 2) {
|
|
const digit = hexString.substr(i, 2)
|
|
const digitNum = parseInt(digit, 16)
|
|
if (digitNum >= 32 && digitNum <= 126) {
|
|
asciiLine += String.fromCharCode(digitNum)
|
|
} else {
|
|
asciiLine += '.'
|
|
}
|
|
if (highlightAt == byteNo) {
|
|
formattedCode += '<b>' + digit + '</b> '
|
|
} else {
|
|
formattedCode += digit + ' '
|
|
}
|
|
if (++x > 16) {
|
|
formattedCode += asciiLine + '\n'
|
|
x = 0
|
|
asciiLine = ''
|
|
}
|
|
byteNo++
|
|
}
|
|
if (x > 0) {
|
|
for (let i = 0; i <= 16 - x; i++) {
|
|
formattedCode += ' '
|
|
}
|
|
formattedCode += asciiLine
|
|
}
|
|
resolve('<h2>Memory Range from 0x' + from.toString(16) + ' to 0x' + to.toString(16) + '</h2><code><pre>' + formattedCode + '</pre></code>')
|
|
conn.destroy()
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
// this method is called when your extension is deactivated
|
|
export function deactivate() { }
|