|
1 |
| -import { ExtensionContext, window, workspace } from 'vscode' |
| 1 | +import { commands, ExtensionContext, ViewColumn, WebviewPanel, window, workspace } from 'vscode' |
2 | 2 |
|
3 | 3 | interface Definitions {
|
4 | 4 | [key: string]: string
|
5 | 5 | }
|
6 | 6 |
|
7 | 7 | export class Dictionary {
|
8 |
| - |
| 8 | + |
9 | 9 | private definitionsFromSettings: Definitions = {};
|
10 | 10 | private definitionsFromComments: Definitions = {};
|
11 | 11 | private definitions: Definitions = {};
|
| 12 | + private panel: WebviewPanel | undefined |
| 13 | + |
| 14 | + constructor(context: ExtensionContext) { |
| 15 | + |
| 16 | + const fileText = window.activeTextEditor?.document.getText(); |
| 17 | + |
| 18 | + const removeLeadingZerosFromKeys = (obj: Definitions): Definitions => { |
| 19 | + Object.keys(obj).forEach(key => { |
| 20 | + let cleanKey = key; |
| 21 | + const prefix = key.replace(/\d+/g, ''); |
| 22 | + if ((key.length > prefix.length) && key.startsWith(prefix)) { |
| 23 | + const suffix = key.substr(prefix.length); |
| 24 | + cleanKey = prefix + parseInt(suffix); |
| 25 | + if (cleanKey.length < key.length && obj[cleanKey] == undefined) { |
| 26 | + obj[cleanKey] = obj[key]; |
| 27 | + delete obj[key]; |
| 28 | + } |
| 29 | + } |
| 30 | + }) |
| 31 | + return obj |
| 32 | + }; |
| 33 | + |
| 34 | + const refreshDefinitionsFromComments = (fileText: string) => { |
| 35 | + |
| 36 | + function parse(comments: string[]): Definitions { |
| 37 | + // Change first and last characters from parens to double-quotes |
| 38 | + comments = comments.map(comment => '"' + comment.substr(1, comment.length - 2) + '"'); |
| 39 | + // Ensure key value seperator is a colon, add inner quotes |
| 40 | + comments = comments.map(comment => comment.replace(/\s*[:=]\s*/, '": "')); |
| 41 | + // Finish converting comment to a JSON string |
| 42 | + const jsonString: string = "{\n" + comments.join(",\n") + "\n}"; |
| 43 | + // Parse the JSON to an object !!! This may result in an error, we have not checked for duplicate keys |
| 44 | + return JSON.parse(jsonString); |
| 45 | + } |
12 | 46 |
|
13 |
| - constructor(fileText?: string) { |
| 47 | + const regex = /\(\s*[A-Z0-9]{2,5}\s*[:=]\s*[a-zA-Z0-9\,\s]+\s*\)/g; |
| 48 | + const matches = fileText.match(regex); |
| 49 | + const obj = (matches == null) ? {} : parse(matches); |
| 50 | + return removeLeadingZerosFromKeys(obj); |
14 | 51 |
|
15 |
| - // Initialize definitions |
16 |
| - this.definitionsFromComments = (fileText == undefined) ? {} : this.refreshDefinitionsFromComments(fileText); |
17 |
| - this.definitionsFromSettings = this.refreshDefinitionsFromSettings(); |
18 |
| - this.definitions = this.mergeDefinitions(); |
19 |
| - } |
| 52 | + } |
| 53 | + |
| 54 | + const refreshDefinitionsFromSettings = () => { |
| 55 | + |
| 56 | + // Get through the proxy object... |
| 57 | + const obj = JSON.parse(JSON.stringify(workspace.getConfiguration('gcode.definitions'))); |
| 58 | + |
| 59 | + return removeLeadingZerosFromKeys(obj); |
| 60 | + } |
| 61 | + |
| 62 | + const mergeDefinitions = () => Object.assign({}, this.definitionsFromSettings, this.definitionsFromComments) |
20 | 63 |
|
| 64 | + const publish = () => { |
| 65 | + this.definitions = mergeDefinitions() |
| 66 | + if (this.panel != undefined) { this.panel.webview.html = getWebviewHtml() } |
| 67 | + } |
| 68 | + |
| 69 | + // Initialize definitions |
| 70 | + this.definitionsFromComments = (fileText == undefined) ? {} : refreshDefinitionsFromComments(fileText); |
| 71 | + this.definitionsFromSettings = refreshDefinitionsFromSettings(); |
| 72 | + publish() |
21 | 73 |
|
22 |
| - public register(context: ExtensionContext): void { |
23 |
| - |
24 |
| - // Register a function to parse comments when the active editor changes |
| 74 | + // Register event handlers |
25 | 75 | context.subscriptions.push(
|
26 |
| - workspace.onWillSaveTextDocument(event => { |
27 |
| - if (event.document.languageId == 'gcode') { |
28 |
| - this.definitionsFromComments = this.refreshDefinitionsFromComments(event.document.getText()); |
29 |
| - this.definitions = this.mergeDefinitions(); |
| 76 | + |
| 77 | + // Parse comments when the active editor changes |
| 78 | + window.onDidChangeActiveTextEditor(event => { |
| 79 | + if (event?.document.languageId == 'gcode') { |
| 80 | + this.definitionsFromComments = refreshDefinitionsFromComments(event.document.getText()); |
| 81 | + publish() |
30 | 82 | }
|
31 |
| - }) |
32 |
| - ); |
| 83 | + }), |
33 | 84 |
|
34 |
| - // Register a function to read definitions from settings when any config file is changed |
35 |
| - context.subscriptions.push( |
| 85 | + // Parse comments when the document is saved |
| 86 | + workspace.onDidSaveTextDocument(document => { |
| 87 | + if (document.languageId == 'gcode') { |
| 88 | + this.definitionsFromComments = refreshDefinitionsFromComments(document.getText()); |
| 89 | + publish() |
| 90 | + } |
| 91 | + }), |
| 92 | + |
| 93 | + // Read definitions from settings when any config file is changed |
36 | 94 | workspace.onDidChangeConfiguration(event => {
|
37 | 95 | if (event.affectsConfiguration('gcode.definitions')) {
|
38 |
| - this.definitionsFromSettings = this.refreshDefinitionsFromSettings(); |
39 |
| - this.definitions = this.mergeDefinitions(); |
| 96 | + this.definitionsFromSettings = refreshDefinitionsFromSettings(); |
| 97 | + publish() |
40 | 98 | }
|
41 |
| - }) |
42 |
| - ); |
43 |
| - } |
| 99 | + }), |
44 | 100 |
|
45 |
| - public lookup(code: string){ |
46 |
| - const removeLeadingZeros = (code: string) => code.substring(0,1) + parseInt(code.substring(1)).toString(); |
47 |
| - return this.definitions[removeLeadingZeros(code)]; |
48 |
| - } |
| 101 | + // Register a command to display the dictionary in a webview |
| 102 | + commands.registerCommand('gcode.showDictionary', () => { |
49 | 103 |
|
50 |
| - private mergeDefinitions() { |
51 |
| - return Object.assign({}, this.definitionsFromSettings, this.definitionsFromComments) |
52 |
| - } |
| 104 | + // Create and show panel |
| 105 | + if (this.panel != undefined) { |
| 106 | + this.panel.reveal(ViewColumn.Beside) |
| 107 | + } |
| 108 | + else { |
| 109 | + this.panel = window.createWebviewPanel( |
| 110 | + 'gcodeDictionary', |
| 111 | + 'G-Code Dictionary', |
| 112 | + ViewColumn.Beside, |
| 113 | + {} |
| 114 | + ); |
| 115 | + this.panel.onDidDispose( |
| 116 | + () => { this.panel = undefined; }, |
| 117 | + undefined, |
| 118 | + context.subscriptions |
| 119 | + ); |
| 120 | + } |
53 | 121 |
|
54 |
| - private refreshDefinitionsFromComments(fileText: string) { |
55 |
| - |
56 |
| - function parse(comments: string[]): Definitions { |
57 |
| - // Change first and last characters from parens to double-quotes |
58 |
| - comments = comments.map(comment => '"' + comment.substr(1, comment.length - 2) + '"'); |
59 |
| - // Ensure key value seperator is a colon, add inner quotes |
60 |
| - comments = comments.map(comment => comment.replace(/\s*[:=]\s*/, '": "')); |
61 |
| - // Finish converting comment to a JSON string |
62 |
| - const jsonString: string = "{\n" + comments.join(",\n") + "\n}"; |
63 |
| - // Parse the JSON to an object !!! This may result in an error, we have not checked for duplicate keys |
64 |
| - return JSON.parse(jsonString); |
65 |
| - } |
| 122 | + // And set its HTML content |
| 123 | + this.panel.webview.html = getWebviewHtml(); |
| 124 | + }) |
| 125 | + |
| 126 | + ) |
| 127 | + |
| 128 | + const getWebviewHtml = () => { |
| 129 | + const template = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>G-Code Dictionary</title></head><body></body></html>'; |
| 130 | + |
| 131 | + const keys = Object.keys(this.definitions).sort(function(a, b){ |
| 132 | + |
| 133 | + function getWord(str: string){ |
| 134 | + const prefix = ((str.match(/^[A-Z]/g)) as RegExpMatchArray)[0] |
| 135 | + return { |
| 136 | + address: prefix, |
| 137 | + value: parseInt(str.replace(prefix, '')) |
| 138 | + } |
| 139 | + } |
66 | 140 |
|
67 |
| - const regex = /\(\s*[A-Z0-9]{2,5}\s*[:=]\s*[a-zA-Z0-9\,\s]+\s*\)/g; |
68 |
| - const matches = fileText.match(regex); |
69 |
| - const obj = (matches == null) ? {} : parse(matches); |
70 |
| - return this.removeLeadingZerosFromKeys(obj); |
| 141 | + const A = getWord(a) |
| 142 | + const B = getWord(b) |
| 143 | + if(A.address > B.address) { |
| 144 | + return 1 |
| 145 | + } |
| 146 | + else if (A.address < B.address) { |
| 147 | + return -1 |
| 148 | + } |
| 149 | + else { |
| 150 | + return A.value - B.value |
| 151 | + } |
| 152 | + }) |
| 153 | + |
| 154 | + const data = keys.map(key => ({ word: key, meaning: this.definitions[key] })) |
| 155 | + const html = (data.map(item => `<p><strong>${item.word}</strong> - ${item.meaning}</p>`)).join('\n') |
| 156 | + return template.replace('<body></body>', `<body>${html}</body>`) |
| 157 | + } |
71 | 158 |
|
72 | 159 | }
|
73 | 160 |
|
74 |
| - private refreshDefinitionsFromSettings() { |
75 |
| - let obj = JSON.parse(JSON.stringify(workspace.getConfiguration('gcode.definitions'))); |
76 |
| - return this.removeLeadingZerosFromKeys(obj); |
| 161 | + public lookup(code: string) { |
| 162 | + const removeLeadingZeros = (code: string) => code.substring(0, 1) + parseInt(code.substring(1)).toString(); |
| 163 | + return this.definitions[removeLeadingZeros(code)]; |
77 | 164 | }
|
78 | 165 |
|
79 |
| - private removeLeadingZerosFromKeys(obj: Definitions): Definitions { |
80 |
| - Object.keys(obj).forEach(key => { |
81 |
| - let cleanKey = key; |
82 |
| - const prefix = key.replace(/\d+/g, ''); |
83 |
| - if ((key.length > prefix.length) && key.startsWith(prefix)) { |
84 |
| - const suffix = key.substr(prefix.length); |
85 |
| - cleanKey = prefix + parseInt(suffix); |
86 |
| - if (cleanKey.length < key.length && obj[cleanKey] == undefined) { |
87 |
| - obj[cleanKey] = obj[key]; |
88 |
| - delete obj[key]; |
89 |
| - } |
90 |
| - } |
91 |
| - }) |
92 |
| - return obj |
93 |
| - } |
94 | 166 | }
|
95 | 167 |
|
96 | 168 |
|
0 commit comments