mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2026-01-01 17:48:36 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a563a3584a | ||
|
|
758aafc570 | ||
|
|
eba90dbbf9 | ||
|
|
47647a05ae | ||
|
|
28e284f0ad | ||
|
|
c26320ec03 | ||
|
|
7d20df3b35 |
@@ -15,22 +15,19 @@
|
||||
{ "include": "#const_def" },
|
||||
{ "include": "#type_declear"},
|
||||
{ "include": "#class_def" },
|
||||
{ "include": "#builtinFuncs" },
|
||||
{ "include": "#builtinClasses" },
|
||||
{ "include": "#builtinProps" },
|
||||
{ "include": "#builtinConsts" },
|
||||
{ "include": "#const_vars" },
|
||||
{ "include": "#classname"},
|
||||
{ "include": "#builtin_func" },
|
||||
{ "include": "#builtin_classes" },
|
||||
{ "include": "#const_vars" },
|
||||
{ "include": "#class_new"},
|
||||
{ "include": "#class_is"},
|
||||
{ "include": "#class_enum"},
|
||||
{ "include": "#function-declaration" },
|
||||
{ "include": "#function-return-type" },
|
||||
{ "include": "#any-method" },
|
||||
{ "include": "#any-property" },
|
||||
{
|
||||
"match": "(?<=extends)\\s+[a-zA-Z_][a-zA-Z_0-9]*(\\.([a-zA-Z_][a-zA-Z_0-9]*))?",
|
||||
"name": "entity.other.inherited-class.gdscript"
|
||||
}
|
||||
{ "include": "#extends" },
|
||||
{ "include": "#parscal_class" }
|
||||
],
|
||||
"repository": {
|
||||
"comment": {
|
||||
@@ -45,17 +42,32 @@
|
||||
"strings": {
|
||||
"patterns": [{
|
||||
"begin": "\"",
|
||||
"end": "(?<!\\\\)\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{ "name": "constant.character.escape.untitled",
|
||||
"match": "\\."
|
||||
}
|
||||
],
|
||||
"name": "string.quoted.double.gdscript"
|
||||
},
|
||||
{
|
||||
"begin": "'",
|
||||
"end": "(?<!\\\\)'",
|
||||
"end": "'",
|
||||
"patterns": [
|
||||
{ "name": "constant.character.escape.untitled",
|
||||
"match": "\\."
|
||||
}
|
||||
],
|
||||
"name": "string.quoted.single.gdscript"
|
||||
},
|
||||
{
|
||||
"begin": "@\"",
|
||||
"end": "(?<!\\\\)\"",
|
||||
"end": "\"",
|
||||
"patterns": [
|
||||
{ "name": "constant.character.escape.untitled",
|
||||
"match": "\\."
|
||||
}
|
||||
],
|
||||
"name": "string.nodepath.gdscript"
|
||||
}
|
||||
]
|
||||
@@ -91,7 +103,7 @@
|
||||
},
|
||||
|
||||
"keywords": {
|
||||
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|in|is|return|onready|setget|enum|match|breakpoint|tool|extends|signal|class|static|export|var|const|func|new|void|float|int|bool|as|assert|class_name|preload|yield|remote|sync|master|puppet|slave|remotesync|mastersync|puppetsync)\\b",
|
||||
"match": "\\b(?i:if|elif|else|for|while|break|continue|pass|return|match|func|class|class_name|extends|is|onready|tool|static|export|setget|const|var|as|void|enum|preload|assert|yield|signal|breakpoint|rpc|sync|master|puppet|slave|remotesync|mastersync|puppetsync)\\b",
|
||||
"name": "keyword.language.gdscript"
|
||||
},
|
||||
"letter": {
|
||||
@@ -154,36 +166,40 @@
|
||||
},
|
||||
"match": "(?<=^class)\\s+([a-zA-Z_]\\w*)\\s*(?=:)"
|
||||
},
|
||||
"classname": {
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.language.gdscript"
|
||||
},
|
||||
"2": {
|
||||
"name": "entity.other.inherited-class.gdscript"
|
||||
}
|
||||
},
|
||||
"match": "^(class_name)\\s+([a-zA-Z_]\\w*)"
|
||||
},
|
||||
"class_new": {
|
||||
"captures": {
|
||||
"1": { "name": "entity.name.type.class.gdscript" },
|
||||
"2": { "name": "storage.type.new.gdscript" }
|
||||
},
|
||||
"match": "\\b([a-zA-Z_][a-zA-Z_0-9]*).(new)\\("
|
||||
},
|
||||
"class_is": {
|
||||
"captures": {
|
||||
"1": { "name": "storage.type.is.gdscript" },
|
||||
"2": { "name": "entity.name.type.class.gdscript" }
|
||||
},
|
||||
"match": "\\s+(is)\\s+([a-zA-Z_][a-zA-Z_0-9]*)"
|
||||
},
|
||||
"class_enum": {
|
||||
"captures": {
|
||||
"1": { "name": "entity.name.type.class.gdscript" },
|
||||
"2": { "name": "constant.language.gdscript" }
|
||||
},
|
||||
"match": "\\b([A-Z][a-zA-Z_0-9]*).([A-Z_0-9]+)"
|
||||
"match": "\\b([A-Z][a-zA-Z_0-9]*)\\.([A-Z_0-9]+)"
|
||||
},
|
||||
"classname": {
|
||||
"match": "(?<=class_name)\\s+([a-zA-Z_][a-zA-Z_0-9]*(\\.([a-zA-Z_][a-zA-Z_0-9]*))?)",
|
||||
"name": "entity.name.type.class.gdscript"
|
||||
},
|
||||
"extends": {
|
||||
"match": "(?<=extends)\\s+[a-zA-Z_][a-zA-Z_0-9]*(\\.([a-zA-Z_][a-zA-Z_0-9]*))?",
|
||||
"name": "entity.other.inherited-class.gdscript"
|
||||
},
|
||||
"builtin_func": {
|
||||
"match": "(?<![^.]\\.|:)\\b(sin|cos|tan|sinh|cosh|tanh|asin|acos|atan|atan2|sqrt|fmod|fposmod|floor|ceil|round|abs|sign|pow|log|exp|is_nan|is_inf|ease|decimals|stepify|lerp|dectime|randomize|randi|randf|rand_range|seed|rand_seed|deg2rad|rad2deg|linear2db|db2linear|max|min|clamp|nearest_po2|weakref|funcref|convert|typeof|type_exists|char|str|print|printt|prints|printerr|printraw|var2str|str2var|var2bytes|bytes2var|range|load|inst2dict|dict2inst|hash|Color8|print_stack|instance_from_id|preload|yield|assert)\\b(?=(\\()([^)]*)(\\)))",
|
||||
"name": "support.function.builtin.gdscript"
|
||||
},
|
||||
"builtinClasses": {
|
||||
"builtin_classes": {
|
||||
"match": "(?<![^.]\\.|:)\\b(Vector2|Vector3|Color|Rect2|Array|Basis|Dictionary|Plane|Quat|RID|Rect3|Transform|Transform2D|AABB|String|Color|NodePath|RID|Object|Dictionary|Array|PoolByteArray|PoolIntArray|PoolRealArray|PoolStringArray|PoolVector2Array|PoolVector3Array|PoolColorArray)\\b",
|
||||
"name": "support.class.library.gdscript"
|
||||
},
|
||||
@@ -308,6 +324,12 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parscal_class": {
|
||||
"captures": {
|
||||
"1": { "name": "entity.name.type.class.gdscript" }
|
||||
},
|
||||
"match": "([A-Z][a-zA-Z_0-9]*)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
package.json
12
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "godot-tools",
|
||||
"displayName": "godot-tools",
|
||||
"icon": "icon.png",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"description": "Tools for game development with godot game engine",
|
||||
"repository": "https://github.com/godotengine/godot-vscode-plugin",
|
||||
"author": "The Godot Engine community",
|
||||
@@ -96,16 +96,20 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/marked": "^0.6.5",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "^10.12.21",
|
||||
"@types/prismjs": "^1.16.0",
|
||||
"@types/ws": "^6.0.1",
|
||||
"tslint": "^5.16.0",
|
||||
"vscode": "^1.1.33",
|
||||
"typescript": "^3.4.5"
|
||||
"typescript": "^3.4.5",
|
||||
"vscode": "^1.1.33"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^5.2.1",
|
||||
"global": "^4.4.0",
|
||||
"marked": "^0.7.0",
|
||||
"prismjs": "^1.17.1",
|
||||
"vscode-languageclient": "^5.2.1",
|
||||
"ws": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export class GodotTools {
|
||||
|
||||
constructor(p_context: vscode.ExtensionContext) {
|
||||
this.context = p_context;
|
||||
this.client = new GDScriptLanguageClient();
|
||||
this.client = new GDScriptLanguageClient(p_context);
|
||||
this.client.watch_status(this.on_client_status_changed.bind(this));
|
||||
this.connection_status = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, RequestMessage, NotificationMessage } from "vscode-languageclient";
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, RequestMessage } from "vscode-languageclient";
|
||||
import { is_debug_mode, get_configuration } from "../utils";
|
||||
import { MessageIO, MessageIOReader, MessageIOWriter } from "./MessageIO";
|
||||
import { ResponseMessage } from "vscode-jsonrpc/lib/messages";
|
||||
import { MessageIO, MessageIOReader, MessageIOWriter, Message } from "./MessageIO";
|
||||
import logger from "../loggger";
|
||||
import { EventEmitter } from "events";
|
||||
type Message = RequestMessage | ResponseMessage | NotificationMessage;
|
||||
import NativeDocumentManager from './NativeDocumentManager';
|
||||
|
||||
function getClientOptions(): LanguageClientOptions {
|
||||
return {
|
||||
@@ -44,11 +43,13 @@ export default class GDScriptLanguageClient extends LanguageClient {
|
||||
|
||||
public io: MessageIO = io;
|
||||
|
||||
private context: vscode.ExtensionContext;
|
||||
private _started : boolean = false;
|
||||
private _status : ClientStatus;
|
||||
private _status_changed_callbacks: ((v : ClientStatus)=>void)[] = [];
|
||||
private _initialize_request: Message = null;
|
||||
private message_handler: MessageHandler = null;
|
||||
private native_doc_manager: NativeDocumentManager = null;
|
||||
|
||||
public get started() : boolean { return this._started; }
|
||||
public get status() : ClientStatus { return this._status; }
|
||||
@@ -67,14 +68,16 @@ export default class GDScriptLanguageClient extends LanguageClient {
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
super(`GDScriptLanguageClient`, serverOptions, getClientOptions());
|
||||
this.context = context;
|
||||
this.status = ClientStatus.PENDING;
|
||||
this.message_handler = new MessageHandler();
|
||||
this.io.on('disconnected', this.on_disconnected.bind(this));
|
||||
this.io.on('connected', this.on_connected.bind(this));
|
||||
this.io.on('message', this.on_message.bind(this));
|
||||
this.io.on('send_message', this.on_send_message.bind(this));
|
||||
this.native_doc_manager = new NativeDocumentManager(this.io);
|
||||
}
|
||||
|
||||
connect_to_server() {
|
||||
|
||||
@@ -3,7 +3,8 @@ import { EventEmitter } from "events";
|
||||
import * as WebSocket from 'ws';
|
||||
import MessageBuffer from "./MessageBuffer";
|
||||
import { AbstractMessageWriter, MessageWriter } from "vscode-jsonrpc/lib/messageWriter";
|
||||
import { Message } from "vscode-jsonrpc";
|
||||
import { RequestMessage, ResponseMessage, NotificationMessage } from "vscode-jsonrpc/lib/messages";
|
||||
export type Message = RequestMessage | ResponseMessage | NotificationMessage;
|
||||
|
||||
export class MessageIO extends EventEmitter {
|
||||
|
||||
|
||||
515
src/lsp/NativeDocumentManager.ts
Normal file
515
src/lsp/NativeDocumentManager.ts
Normal file
@@ -0,0 +1,515 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { EventEmitter } from "events";
|
||||
import { MessageIO } from "./MessageIO";
|
||||
import { NotificationMessage } from "vscode-jsonrpc";
|
||||
import { DocumentSymbol } from "vscode";
|
||||
import * as Prism from "prismjs";
|
||||
import * as marked from "marked";
|
||||
marked.setOptions({
|
||||
highlight: function (code, lang) {
|
||||
return Prism.highlight(code, GDScriptGrammar, lang);
|
||||
}
|
||||
});
|
||||
|
||||
const enum Methods {
|
||||
SHOW_NATIVE_SYMBOL = 'gdscript/show_native_symbol',
|
||||
INSPECT_NATIVE_SYMBOL = 'textDocument/nativeSymbol'
|
||||
}
|
||||
|
||||
interface NativeSymbolInspectParams {
|
||||
native_class: string;
|
||||
symbol_name: string;
|
||||
}
|
||||
|
||||
const enum WebViewMessageType {
|
||||
INSPECT_NATIVE_SYMBOL = 'INSPECT_NATIVE_SYMBOL',
|
||||
};
|
||||
|
||||
class GodotNativeSymbol extends DocumentSymbol {
|
||||
documentation: string;
|
||||
native_class: string;
|
||||
};
|
||||
|
||||
export default class NativeDocumentManager extends EventEmitter {
|
||||
|
||||
private io: MessageIO = null;
|
||||
|
||||
constructor(io: MessageIO) {
|
||||
super();
|
||||
this.io = io;
|
||||
io.on("message", (message: NotificationMessage)=>{
|
||||
if (message.method == Methods.SHOW_NATIVE_SYMBOL) {
|
||||
this.show_native_symbol(message.params);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private inspect_native_symbol(params: NativeSymbolInspectParams) {
|
||||
this.io.send_message(JSON.stringify({
|
||||
id: -1,
|
||||
jsonrpc: "2.0",
|
||||
method: Methods.INSPECT_NATIVE_SYMBOL,
|
||||
params
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
private show_native_symbol(symbol: GodotNativeSymbol) {
|
||||
// 创建webview
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'doc',
|
||||
symbol.name,
|
||||
vscode.ViewColumn.Nine,
|
||||
{
|
||||
enableScripts: true, // 启用JS,默认禁用
|
||||
retainContextWhenHidden: false, // webview被隐藏时保持状态,避免被重置
|
||||
}
|
||||
);
|
||||
panel.title = symbol.name;
|
||||
panel.webview.html = this.make_html_content(symbol);
|
||||
panel.webview.onDidReceiveMessage(this.on_webview_message.bind(this));
|
||||
}
|
||||
|
||||
private on_webview_message(msg: any) {
|
||||
switch (msg.type) {
|
||||
case WebViewMessageType.INSPECT_NATIVE_SYMBOL:
|
||||
this.inspect_native_symbol(msg.data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private make_html_content(symbol: GodotNativeSymbol): string {
|
||||
return `
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
${PrismStyleSheet}
|
||||
.codeblock {
|
||||
padding: 0.5em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
!background-color: #fdf6e3;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="line-height: 16pt;">${this.make_symbol_document(symbol)}</body>
|
||||
<script>
|
||||
var vscode = acquireVsCodeApi();
|
||||
function inspect(native_class, symbol_name) {
|
||||
if (typeof(godot_class) != 'undefined' && godot_class == native_class) {
|
||||
document.getElementById(symbol_name).scrollIntoView();
|
||||
} else {
|
||||
vscode.postMessage({
|
||||
type: '${WebViewMessageType.INSPECT_NATIVE_SYMBOL}',
|
||||
data: {
|
||||
native_class: native_class,
|
||||
symbol_name: symbol_name
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
|
||||
private make_symbol_document(symbol: GodotNativeSymbol): string {
|
||||
|
||||
function make_function_signature(s: GodotNativeSymbol) {
|
||||
let parts = /\((.*)?\)\s*\-\>\s*(([A-z0-9]+)?)$/.exec(s.detail);
|
||||
if (!parts) return "";
|
||||
const ret_type = make_link(parts[2] || "void", undefined);
|
||||
let args = (parts[1] || "").replace(/\:\s([A-z0-9_]+)(\,\s*)?/g, `: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`);
|
||||
args = args.replace(/\s=\s(.*?)[\,\)]/g, "")
|
||||
return `${ret_type} ${element("a", s.name, {href: `#${s.name}`})}( ${args} )`;
|
||||
};
|
||||
|
||||
function make_symbol_elements(s: GodotNativeSymbol): {index?: string, body: string} {
|
||||
switch (s.kind) {
|
||||
case vscode.SymbolKind.Property:
|
||||
case vscode.SymbolKind.Variable: {
|
||||
// var Control.anchor_left: float
|
||||
const parts = /\.([A-z_0-9]+)\:\s(.*)$/.exec(s.detail);
|
||||
if (!parts) return;
|
||||
let type = make_link(parts[2], undefined);
|
||||
let name = element("a", s.name, {href: `#${s.name}`});
|
||||
const title = element('h4', type + " " + s.name);
|
||||
const doc = element("p", format_documentation(s.documentation, symbol.native_class));
|
||||
const div = element("div", title + doc);
|
||||
return {
|
||||
index: type + " " + name,
|
||||
body: div,
|
||||
};
|
||||
} break;
|
||||
case vscode.SymbolKind.Constant: {
|
||||
// const Control.FOCUS_ALL: FocusMode = 2
|
||||
// const Control.NOTIFICATION_RESIZED = 40
|
||||
const parts = /\.([A-Za-z_0-9]+)(\:\s*)?([A-z0-9_\.]+)?\s*=\s*(.*)$/.exec(s.detail);
|
||||
if (!parts) return;
|
||||
let type = make_link(parts[3] || 'int', undefined);
|
||||
let name = parts[1];
|
||||
let value = element('code', parts[4]);
|
||||
|
||||
const title = element('p', type + " " + name + " = " + value);
|
||||
const doc = element("p", format_documentation(s.documentation, symbol.native_class));
|
||||
const div = element("div", title + doc);
|
||||
return {
|
||||
body: div
|
||||
};
|
||||
} break;
|
||||
case vscode.SymbolKind.Event: {
|
||||
const parts = /\.([A-z0-9]+)\((.*)?\)/.exec(s.detail);
|
||||
if (!parts) return;
|
||||
const args = (parts[2] || "").replace(/\:\s([A-z0-9_]+)(\,\s*)?/g, `: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`);
|
||||
const title = element('p', `${s.name}( ${args} )`);
|
||||
const doc = element("p", format_documentation(s.documentation, symbol.native_class));
|
||||
const div = element("div", title + doc);
|
||||
return {
|
||||
body: div
|
||||
};
|
||||
} break;
|
||||
case vscode.SymbolKind.Method:
|
||||
case vscode.SymbolKind.Function: {
|
||||
const signature = make_function_signature(s);
|
||||
const title = element("h4", signature);
|
||||
const doc = element("p", format_documentation(s.documentation, symbol.native_class));
|
||||
const div = element("div", title + doc);
|
||||
return {
|
||||
index: signature,
|
||||
body: div
|
||||
};
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (symbol.kind == vscode.SymbolKind.Class) {
|
||||
|
||||
let doc = element("h2", `Native class ${symbol.name}`);
|
||||
const parts = /extends\s+([A-z0-9]+)/.exec(symbol.detail);
|
||||
let inherits = parts && parts.length > 1 ? parts[1] : '';
|
||||
if (inherits) {
|
||||
inherits = `Inherits ${make_link(inherits, undefined)}`;
|
||||
doc += element("p", inherits);
|
||||
}
|
||||
|
||||
let constants = "";
|
||||
let signals = "";
|
||||
let methods_index = "";
|
||||
let methods = "";
|
||||
let properties_index = "";
|
||||
let propertyies = "";
|
||||
let others = "";
|
||||
|
||||
for (let s of symbol.children as GodotNativeSymbol[]) {
|
||||
const elements = make_symbol_elements(s);
|
||||
switch (s.kind) {
|
||||
case vscode.SymbolKind.Property:
|
||||
case vscode.SymbolKind.Variable:
|
||||
properties_index += element("li", elements.index);
|
||||
propertyies += element("li", elements.body, {id: s.name});
|
||||
break;
|
||||
case vscode.SymbolKind.Constant:
|
||||
constants += element("li", elements.body, {id: s.name});
|
||||
break;
|
||||
case vscode.SymbolKind.Event:
|
||||
signals += element("li", elements.body, {id: s.name});
|
||||
break;
|
||||
case vscode.SymbolKind.Method:
|
||||
case vscode.SymbolKind.Function:
|
||||
methods_index += element("li", elements.index);
|
||||
methods += element("li", elements.body, {id: s.name});
|
||||
break;
|
||||
default:
|
||||
others += element("li", elements.body, {id: s.name});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function add_group(title: string, block: string) {
|
||||
if (block) {
|
||||
doc += element('h3', title);
|
||||
doc += element('ul', block);
|
||||
}
|
||||
};
|
||||
add_group("Properties", properties_index);
|
||||
add_group("Constants", constants);
|
||||
add_group("Signals", signals);
|
||||
add_group("Methods", methods_index);
|
||||
add_group("Property Descriptions", propertyies);
|
||||
add_group("Method Descriptions", methods);
|
||||
add_group("Other Members", others);
|
||||
doc += element("script", `var godot_class = "${symbol.native_class}";`);
|
||||
|
||||
return doc;
|
||||
} else {
|
||||
let doc = "";
|
||||
const elements = make_symbol_elements(symbol);
|
||||
if (elements.index) {
|
||||
doc += element("h2", elements.index);
|
||||
}
|
||||
doc += element("div", elements.body);
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function element<K extends keyof HTMLElementTagNameMap>(tag: K, content: string, props = {}, new_line?: boolean, indent?:string) {
|
||||
let props_str = "";
|
||||
for (const key in props) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
props_str += ` ${key}="${props[key]}"`;
|
||||
}
|
||||
}
|
||||
return `${indent || ''}<${tag} ${props_str}>${content}</${tag}>${new_line ? '\n' : ''}`;
|
||||
}
|
||||
function make_link(classname: string, symbol: string) {
|
||||
if (!symbol || symbol == classname) {
|
||||
return element('a', classname, {onclick: `inspect('${classname}', '${classname}')`, href: ''});
|
||||
} else {
|
||||
return element('a', `${classname}.${symbol}`, {onclick: `inspect('${classname}', '${symbol}')`, href: ''});
|
||||
}
|
||||
}
|
||||
|
||||
function make_codeblock(code: string) {
|
||||
const md = marked('```gdscript\n' + code + '\n```');
|
||||
return `<div class="codeblock">${md}</div>`;
|
||||
}
|
||||
|
||||
function format_documentation(p_bbcode: string, classname: string) {
|
||||
let html = p_bbcode.trim();
|
||||
let lines = html.split("\n");
|
||||
let in_code_block = false;
|
||||
let code_block_indent = -1;
|
||||
let cur_code_block = "";
|
||||
|
||||
html = "";
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
let block_start = line.indexOf("[codeblock]");
|
||||
if (block_start != -1) {
|
||||
code_block_indent = block_start;
|
||||
in_code_block = true;
|
||||
line = line.replace("[codeblock]", "");
|
||||
} else if (in_code_block) {
|
||||
line = line.substr(code_block_indent, line.length);
|
||||
}
|
||||
|
||||
if (in_code_block && line.indexOf("[/codeblock]") != -1) {
|
||||
line = line.replace("[/codeblock]", "");
|
||||
in_code_block = false;
|
||||
html += make_codeblock(cur_code_block);
|
||||
cur_code_block = "";
|
||||
}
|
||||
|
||||
if (!in_code_block) {
|
||||
line = line.trim();
|
||||
// [i] [/u] [code] --> <i> </u> <code>
|
||||
line = line.replace(/(\[(\/?)([a-z]+)\])/g, `<$2$3>`);
|
||||
// [Reference] --> <a>Reference</a>
|
||||
line = line.replace(/(\[([A-Z]+[A-Z_a-z0-9]*)\])/g, `<a href="" onclick="inspect('$2', '$2')">$2</a>`);
|
||||
// [method _set] --> <a>_set</a>
|
||||
line = line.replace(/(\[([a-z]+)\s+([A-Z_a-z][A-Z_a-z0-9]*)\])/g, `<a href="" onclick="inspect('${classname}', '$3')">$3</a>`);
|
||||
line += "<br/>";
|
||||
html += line;
|
||||
} else {
|
||||
line += "\n";
|
||||
if (cur_code_block || line.trim()) {
|
||||
cur_code_block += line;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
const GDScriptGrammar = {
|
||||
'comment': {
|
||||
pattern: /(^|[^\\])#.*/,
|
||||
lookbehind: true
|
||||
},
|
||||
'string-interpolation': {
|
||||
pattern: /(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'interpolation': {
|
||||
// "{" <expression> <optional "!s", "!r", or "!a"> <optional ":" format specifier> "}"
|
||||
pattern: /((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,
|
||||
lookbehind: true,
|
||||
inside: {
|
||||
'format-spec': {
|
||||
pattern: /(:)[^:(){}]+(?=}$)/,
|
||||
lookbehind: true
|
||||
},
|
||||
'conversion-option': {
|
||||
pattern: //,
|
||||
alias: 'punctuation'
|
||||
},
|
||||
rest: null
|
||||
}
|
||||
},
|
||||
'string': /[\s\S]+/
|
||||
}
|
||||
},
|
||||
'triple-quoted-string': {
|
||||
pattern: /(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,
|
||||
greedy: true,
|
||||
alias: 'string'
|
||||
},
|
||||
'string': {
|
||||
pattern: /(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,
|
||||
greedy: true
|
||||
},
|
||||
'function': {
|
||||
pattern: /((?:^|\s)func[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,
|
||||
lookbehind: true
|
||||
},
|
||||
'class-name': {
|
||||
pattern: /(\bclass\s+)\w+/i,
|
||||
lookbehind: true
|
||||
},
|
||||
'decorator': {
|
||||
pattern: /(^\s*)@\w+(?:\.\w+)*/im,
|
||||
lookbehind: true,
|
||||
alias: ['annotation', 'punctuation'],
|
||||
inside: {
|
||||
'punctuation': /\./
|
||||
}
|
||||
},
|
||||
'keyword': /\b(?:if|elif|else|for|while|break|continue|pass|return|match|func|class|class_name|extends|is|onready|tool|static|export|setget|const|var|as|void|enum|preload|assert|yield|signal|breakpoint|rpc|sync|master|puppet|slave|remotesync|mastersync|puppetsync)\b/,
|
||||
'builtin': /\b(?:PI|TAU|NAN|INF|_|sin|cos|tan|sinh|cosh|tanh|asin|acos|atan|atan2|sqrt|fmod|fposmod|floor|ceil|round|abs|sign|pow|log|exp|is_nan|is_inf|ease|decimals|stepify|lerp|dectime|randomize|randi|randf|rand_range|seed|rand_seed|deg2rad|rad2deg|linear2db|db2linear|max|min|clamp|nearest_po2|weakref|funcref|convert|typeof|type_exists|char|str|print|printt|prints|printerr|printraw|var2str|str2var|var2bytes|bytes2var|range|load|inst2dict|dict2inst|hash|Color8|print_stack|instance_from_id|preload|yield|assert|Vector2|Vector3|Color|Rect2|Array|Basis|Dictionary|Plane|Quat|RID|Rect3|Transform|Transform2D|AABB|String|Color|NodePath|RID|Object|Dictionary|Array|PoolByteArray|PoolIntArray|PoolRealArray|PoolStringArray|PoolVector2Array|PoolVector3Array|PoolColorArray)\b/,
|
||||
'boolean': /\b(?:true|false)\b/,
|
||||
'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,
|
||||
'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,
|
||||
'punctuation': /[{}[\];(),.:]/
|
||||
};
|
||||
|
||||
const PrismStyleSheet = `
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #657b83; /* base00 */
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
background: #073642; /* base02 */
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background-color: #fdf6e3; /* base3 */
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #93a1a1; /* base1 */
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #586e75; /* base01 */
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #268bd2; /* blue */
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.url,
|
||||
.token.inserted {
|
||||
color: #2aa198; /* cyan */
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
color: #657b83; /* base00 */
|
||||
background: #eee8d5; /* base2 */
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #859900; /* green */
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #b58900; /* yellow */
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #cb4b16; /* orange */
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
`;
|
||||
@@ -4,13 +4,14 @@
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
"es2020",
|
||||
"dom"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": false
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user