diff --git a/src/document_link_provider.ts b/src/document_link_provider.ts new file mode 100644 index 0000000..0b07486 --- /dev/null +++ b/src/document_link_provider.ts @@ -0,0 +1,47 @@ +import * as vscode from "vscode"; +import { Uri, Position, Range, TextDocument } from "vscode"; + +export class GDDocumentLinkProvider implements vscode.DocumentLinkProvider { + private context: vscode.ExtensionContext; + + constructor(context: vscode.ExtensionContext) { + this.context = context; + + const disp = vscode.languages.registerDocumentLinkProvider( + ["gdresource"], + this + ); + + context.subscriptions.push(disp); + } + + async provideDocumentLinks( + document: vscode.TextDocument, + token: vscode.CancellationToken + ): Promise { + let links = []; + let lines = document.getText().split("\n"); + for (let i = 0; i < lines.length; i++) { + const match = lines[i].match(/res:\/\/[^"^']*/); + if (match) { + const start = new Position(i, match.index); + const end = new Position(i, match.index + match[0].length); + const r = new Range(start, end); + const uri = await resourcePathToUri(match[0]); + if (uri instanceof Uri) { + links.push(new vscode.DocumentLink(r, uri)); + } + } + } + return links; + } +} + +async function resourcePathToUri(resPath: string) { + const files = await vscode.workspace.findFiles("**/project.godot"); + if (!files) { + return resPath; + } + const project_dir = files[0].fsPath.replace("project.godot", ""); + return Uri.joinPath(Uri.file(project_dir), resPath.substring(6)); +} diff --git a/src/godot-tools.ts b/src/godot-tools.ts index 0aef7c0..15740d3 100644 --- a/src/godot-tools.ts +++ b/src/godot-tools.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import * as path from 'path'; import * as fs from 'fs'; +import { GDDocumentLinkProvider } from "./document_link_provider"; import GDScriptLanguageClient, { ClientStatus } from "./lsp/GDScriptLanguageClient"; import { get_configuration, set_configuration } from "./utils"; @@ -11,6 +12,8 @@ export class GodotTools { private reconnection_attempts = 0; private context: vscode.ExtensionContext; private client: GDScriptLanguageClient = null; + private linkProvider: GDDocumentLinkProvider = null; + // deprecated, need to replace with "vscode.workspace.workspaceFolders", but // that's an array and not a single value private workspace_dir = vscode.workspace.rootPath; @@ -25,6 +28,8 @@ export class GodotTools { this.client.watch_status(this.on_client_status_changed.bind(this)); this.connection_status = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); + this.linkProvider = new GDDocumentLinkProvider(p_context); + setInterval(() => { this.retry_callback(); }, get_configuration("reconnect_cooldown", 3000)); diff --git a/src/lsp/GDScriptLanguageClient.ts b/src/lsp/GDScriptLanguageClient.ts index bd63881..002d154 100644 --- a/src/lsp/GDScriptLanguageClient.ts +++ b/src/lsp/GDScriptLanguageClient.ts @@ -97,6 +97,19 @@ export default class GDScriptLanguageClient extends LanguageClient { if (is_debug_mode()) { logger.log("[server]", JSON.stringify(message)); } + + // This is a dirty hack to fix the language server sending us + // invalid file URIs + // This should be forward-compatible, meaning that it will work + // with the current broken version, AND the fixed future version. + const match = JSON.stringify(message).match(/"target":"file:\/\/[^\/][^"]*"/); + if (match) { + for (let i = 0; i < message["result"].length; i++) { + const x = message["result"][i]["target"]; + message["result"][i]["target"] = x.replace('file://', 'file:///'); + } + } + this.message_handler.on_message(message); } diff --git a/syntaxes/examples/Example.gd b/syntaxes/examples/Example.gd new file mode 100644 index 0000000..38c4d54 --- /dev/null +++ b/syntaxes/examples/Example.gd @@ -0,0 +1,4 @@ +extends Node + +func _ready(): + pass diff --git a/syntaxes/examples/Example.tscn b/syntaxes/examples/Example.tscn new file mode 100644 index 0000000..a135492 --- /dev/null +++ b/syntaxes/examples/Example.tscn @@ -0,0 +1,93 @@ +[gd_scene load_steps=7 format=2] + +[ext_resource path="res://Example.gd" type="Script" id=1] + +[sub_resource type="Environment" id=1] +background_mode = 4 +tonemap_mode = 3 +glow_enabled = true +glow_blend_mode = 0 + +[sub_resource type="Animation" id=2] +resource_name = "RESET" +tracks/0/type = "value" +tracks/0/path = NodePath("CanvasLayer/Panel:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0 ), +"transitions": PoolRealArray( 1 ), +"update": 0, +"values": [ Color( 0, 0, 0, 0 ) ] +} + +[sub_resource type="Animation" id=3] +resource_name = "dim" +tracks/0/type = "value" +tracks/0/path = NodePath("CanvasLayer/Panel:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ Color( 0, 0, 0, 0 ), Color( 0, 0, 0, 0.501961 ) ] +} + +[sub_resource type="Animation" id=4] +tracks/0/type = "value" +tracks/0/path = NodePath("CanvasLayer/Panel:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ Color( 0, 0, 0, 1 ), Color( 0, 0, 0, 0 ) ] +} + +[sub_resource type="Animation" id=5] +tracks/0/type = "value" +tracks/0/path = NodePath("CanvasLayer/Panel:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/keys = { +"times": PoolRealArray( 0, 1 ), +"transitions": PoolRealArray( 1, 1 ), +"update": 0, +"values": [ Color( 0, 0, 0, 0 ), Color( 0, 0, 0, 1 ) ] +} + +[node name="Main" type="Node"] +script = ExtResource( 1 ) + +[node name="World" type="Node2D" parent="."] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource( 1 ) + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +layer = 128 + +[node name="Panel" type="Panel" parent="CanvasLayer"] +modulate = Color( 0, 0, 0, 0 ) +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 2 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="FadePlayer" type="AnimationPlayer" parent="."] +anims/RESET = SubResource( 2 ) +anims/dim = SubResource( 3 ) +anims/fade_in = SubResource( 4 ) +anims/fade_out = SubResource( 5 ) diff --git a/syntaxes/examples/project.godot b/syntaxes/examples/project.godot new file mode 100644 index 0000000..e69de29 diff --git a/syntaxes/examples/rainbow_gradient_shader.tres b/syntaxes/examples/rainbow_gradient_shader.tres new file mode 100644 index 0000000..f39183d --- /dev/null +++ b/syntaxes/examples/rainbow_gradient_shader.tres @@ -0,0 +1,41 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=2] + +[sub_resource type="Shader" id=63] +code = "// HSV to RBG from https://www.rapidtables.com/convert/color/hsv-to-rgb.html +// Rotation matrix from https://en.wikipedia.org/wiki/Rotation_matrix + +shader_type canvas_item; + +const float PI = 3.1415926535; + +uniform float strength: hint_range(0., 1.) = 0.5; +uniform float speed: hint_range(0., 10.) = 0.5; +uniform float angle: hint_range(0., 360.) = 0.; + +void fragment() { + float hue = UV.x * cos(radians(angle)) - UV.y * sin(radians(angle)); + hue = fract(hue + fract(TIME * speed)); + float x = 1. - abs(mod(hue / (1./ 6.), 2.) - 1.); + vec3 rainbow; + if(hue < 1./6.){ + rainbow = vec3(1., x, 0.); + } else if (hue < 1./3.) { + rainbow = vec3(x, 1., 0); + } else if (hue < 0.5) { + rainbow = vec3(0, 1., x); + } else if (hue < 2./3.) { + rainbow = vec3(0., x, 1.); + } else if (hue < 5./6.) { + rainbow = vec3(x, 0., 1.); + } else { + rainbow = vec3(1., 0., x); + } + vec4 color = texture(TEXTURE, UV); + COLOR = mix(color, vec4(rainbow, color.a), strength); +}" + +[resource] +shader = SubResource( 63 ) +shader_param/strength = 0.5 +shader_param/speed = 0.5 +shader_param/angle = 0.0