Giter Club home page Giter Club logo

gdlinter's Introduction

GDLinter is an addon for Godot Engine that runs gdlint on save to automatically lint your GDScript as you code.

‼️ Prerequisites

  1. Make sure you have gdtoolkit installed

📦 Installation

  1. Download Latest Release
  2. Unpack the addons/gdLinter folder into your /addons folder within the Godot project
  3. Enable this addon within the Godot settings: Project > Project Settings > Plugins

🙏 Special thanks

  • @Scony (for creating gdlint)

gdlinter's People

Contributors

el-falso avatar pongsixtyfour avatar nullaf avatar perroboc avatar

Stargazers

Masaharu Hosomichi avatar  avatar  avatar nightblade9 avatar  avatar  avatar  avatar  avatar Nikita Bednyakov avatar  avatar 深淵の鴿子 avatar  avatar Nikolaus Pöttler avatar Ryan Helsing avatar

Watchers

 avatar  avatar

gdlinter's Issues

Loading error

System Info

Godot v4.2.1.stable - Fedora Linux 39 (KDE Plasma) - Wayland - Vulkan (Forward+) - dedicated AMD Radeon RX 7900 XTX (RADV NAVI31) () - AMD Ryzen 9 7950X 16-Core Processor (32 Threads)

Issue

I get the following error after enabling the plugin:

res://addons/gdLinter/gdLinter.gd:105 - Invalid access to property or key 'strings' on a base object of type 'null instance'.
Loading GDLint Plugin success

Additional info

Installed gdtoolkit using pipx, and had to manually install setuptools in its virtual environment, since I was getting this error: ModuleNotFoundError: No module named 'pkg_resources' (According to this StackOverflow post)

$ pipx install gdtoolkit==4.*
  installed package gdtoolkit 4.2.2, installed using Python 3.12.2
  These apps are now globally available
    - gd2py
    - gdformat
    - gdlint
    - gdparse
    - gdradon
done! ✨ 🌟 ✨
$ pipx inject gdtoolkit setuptools
  injected package setuptools into venv gdtoolkit
done! ✨ 🌟 ✨
$ gdlint --version
gdlint 4.2.2

Source files do not conform to style guide

The source files for the plugin do not entirely conform to the style guide. This isn't a major issue for functionality, but it does ironically make it impossible for a project to be completely style-guide compliant while using this plugin, which seems somewhat self-defeating.

gdlint reports the following problems:

./gdLinter.gd:35: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:38: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:75: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:83: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:86: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:89: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:98: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:101: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:108: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:114: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:128: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:131: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:150: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:174: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:181: Error: Trailing whitespace(s) (trailing-whitespace)
./gdLinter.gd:186: Error: Trailing whitespace(s) (trailing-whitespace)
./error_descriptions.gd:5: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:9: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:10: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:12: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:13: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:14: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:18: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:22: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:23: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:24: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:25: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:26: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:31: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:49: Error: Max allowed line length (100) exceeded (max-line-length)
./error_descriptions.gd:50: Error: Max allowed line length (100) exceeded (max-line-length)
./UI/Name.gd:31: Error: Max allowed line length (100) exceeded (max-line-length)
./UI/Dock.gd:54: Error: Trailing whitespace(s) (trailing-whitespace)
./UI/Dock.gd:59: Error: Trailing whitespace(s) (trailing-whitespace)
./UI/Dock.gd:81: Error: Trailing whitespace(s) (trailing-whitespace)
Failure: 35 problems found

In addition to the problems listed above, file names in the project source use inconsistent casing conventions. The style guide suggests using snake_case for file names.

Dock : Line number is truncated ("131" becomes "13")

Hi !

I've just installed the project and stumbled upon an issue : it seems that, in long files, the line number is shown incorrectly.

In-engine screenshot :

Plugin reports an error at line 13, while the error is at line 131.

Manually running gdlint in terminal :

The original terminal program correctly reports an error at line 131

As we can see, gdlint reports the correct line number (131), while the plugin truncates it to the first 2 characters (13).


Godot : v4.2.2.stable.official [15073afe3]
GDLint Plugin : 2.0.2, installed in-engine through AssetLib
gdlint : 4.2.2, installed with pip3

Thanks for the plugin ! 🎉

recurrent error " Invalid get index 'strings' (on base: 'null instance')."

I'm creating a project in Godot 4.2.1 with the latest version of your add on available on the Asset lib

The following error occurs very often when I'm editing a GD script file and I can"t really found what causes it.
It appears "randomly", not each time I open a script.

  res://addons/gdLinter/gdLinter.gd:105 - Invalid get index 'strings' (on base: 'null instance').
  	Exit code: 1
  Format GDScript failed: res://src/scripts/game_manager.gd
  res://addons/gdLinter/gdLinter.gd:105 - Invalid get index 'strings' (on base: 'null instance').
(3) ["Success: no problems found\r", ""]

My project is just a simple scene with a Node2D (Main) as root, and a child Node (GameManager) with a script attached
I just opened the script it and I have the error in the output panel

image

AINPCs_ISSUE_1.zip
AINPCs_ISSUE_2.zip

I've joined a simpler version of my project.

rename
AINPCs_ISSUE_1.zip to AINPCs_ISSUE.zip.001
AINPCs_ISSUE_2.zip to AINPCs_ISSUE.zip.002
and unzip to get the sources of the project

Can't find gdlint

The problem happens with 2.0.0 from the AssetLib but also with current master.

Version 1.0.1 still works.

I'm using Godot 4.3.dev5.

No VirtualEnv support

It looks like this expects gdlint to be available via PATH by default, which isn't always the case. And it's generally bad practice to do global pip installs, Although this doesn't matter as much on Windows from my PoV. (Because Windows doesn't rely on Python for anything, where MacOS and Linux do)

This isn't taking into account someone having godot tooling isolated to a specific virtualenv.

I think it'd be ideal if one wouldn't need to edit the source code in order to allow this to work with virtualenvs.

I think it'd best to be able to optionally provide GDLINT_PATH from the plugin.cfg file
Or an optional VIRTUALENV_DIR that would point to /Users/jack/.virtualenvs/godot in my case
and essentially have the same effect as my hard-coded changes:

NOTE: The only thing I changed is the GDLINT_PATH
NOTE: My version of gdLinter.gd appears to be outdated, was going to submit a PR before I realized this

@tool
extends EditorPlugin

const GDLINT_PATH: String = "/Users/jack/.virtualenvs/godot/bin/gdlint"

const DockScene := preload("res://addons/gdLinter/UI/Dock.tscn")

var icon_error := EditorInterface.get_editor_theme().get_icon("Error", "EditorIcons")
var color_error: Color = EditorInterface.get_editor_settings()\
		.get_setting("text_editor/theme/highlighting/comment_markers/critical_color")

var icon_success := EditorInterface.get_editor_theme().get_icon("StatusSuccess", "EditorIcons")
var color_success: Color = EditorInterface.get_editor_settings()\
	.get_setting("text_editor/theme/highlighting/comment_markers/notice_color")

var bottom_panel_button: Button
var highlight_lines: PackedInt32Array
var item_lists: Array[ItemList]

var _dock_ui: GDLinterDock
var _is_gdlint_installed: bool


func _enter_tree() -> void:
	# install the GDLint dock
	_dock_ui = DockScene.instantiate()
	bottom_panel_button = add_control_to_bottom_panel(_dock_ui, GDLINT_PATH)
	
	# connect signal to lint on save
	resource_saved.connect(on_resource_saved)
	await get_tree().create_timer(1.0).timeout # Workaround so the script editor gets loaded
	var current_open_script = load(ProjectSettings.globalize_path(
				EditorInterface.get_script_editor().get_current_script().resource_path)
		) as Resource
	on_resource_saved(current_open_script)
	
	var script_editor := EditorInterface.get_script_editor()
	script_editor.editor_script_changed.connect(_on_editor_script_changed)
	get_gdlint_version()
	
	get_item_list(script_editor)

	prints("Loading GDLint Plugin success")


# Dunno how highlighting lines in Godot works, since it get removed after a second or so
# So I use this evil workaround straight from hell:
func _process(_delta: float) -> void:
	if not highlight_lines.is_empty():
		set_line_color(color_error)


func _on_editor_script_changed(_script: Script) -> void:
	var current_open_script = load(ProjectSettings.globalize_path(
			EditorInterface.get_script_editor().get_current_script().resource_path)) as Resource
	on_resource_saved(current_open_script)


func get_gdlint_version() -> void:
	var output := []
	OS.execute(GDLINT_PATH, ["--version"], output)
	_is_gdlint_installed = true if not output[0].is_empty() else false
	if _is_gdlint_installed:
		_dock_ui.version.text = "Using %s" % output[0]
	else:
		_dock_ui.version.text = "gdlint not found!"


func _exit_tree() -> void:
	if is_instance_valid(_dock_ui):
		remove_control_from_bottom_panel(_dock_ui)
		_dock_ui.free()
	
	if Engine.get_version_info().hex >= 0x40201:
		prints("Unload GDLint Plugin success")


func on_resource_saved(resource: Resource):
	_dock_ui.delete_errors()
	clear_highlights()
	
	# Show resource path in the GDLint Dock
	_dock_ui.file.text = resource.resource_path
	
	# Execute linting and get its output
	var filepath: String = ProjectSettings.globalize_path(resource.resource_path)
	var gdlint_output: Array = []
	var output_array: PackedStringArray
	var exit_code = OS.execute(GDLINT_PATH, [filepath], gdlint_output, true)
	if not exit_code == -1:
		var output_string: String = gdlint_output[0]
		output_array = output_string.replace(filepath+":", "Line ").split("\n")
	
	# When there is no error
	if output_array.size() <= 2:
		print(output_array)
		_dock_ui.label.text = output_array[0]
		_dock_ui.label.modulate = color_success
		bottom_panel_button.icon = icon_success
	
	# When errors are found create buttons in the dock
	else:
		for i in output_array.size()-2:
			var regex := RegEx.new()
			regex.compile("\\d+")
			var result := regex.search(output_array[i])
			var current_line := int(result.strings[0])-1
			highlight_lines.append(current_line)

			var button: Button = _dock_ui.create_error(output_array[i])
			button.pressed.connect(go_to_line.bind(current_line))

		_dock_ui.label.text = output_array[output_array.size()-2]
		_dock_ui.label.modulate = Color(255, 255, 255)
		bottom_panel_button.icon = icon_error
		_dock_ui.script_text_editor = EditorInterface.get_script_editor().get_current_editor()


func go_to_line(line: int) -> void:
	var tab_container := _dock_ui.script_text_editor.get_parent() as TabContainer
	
	for index in tab_container.get_child_count():
		if tab_container.get_child(index) == _dock_ui.script_text_editor:
			item_lists[0].select(index)
			item_lists[0].item_selected.emit(index)
	
	var current_code_editor := get_current_editor()
	current_code_editor.set_caret_line(line)


func get_item_list(node: Node) -> void:
	for child: Node in node.get_children():
		if not child is ItemList:
			if child is Window:
				break
			get_item_list(child)
		else:
			item_lists.append(child)


func set_line_color(color: Color) -> void:
	for line: int in highlight_lines:
		var current_code_editor := get_current_editor()
		current_code_editor.set_line_background_color(line,
			color.darkened(0.5))


func clear_highlights() -> void:
	set_line_color(Color(0, 0, 0, 0))
	highlight_lines.clear()


func get_current_editor() -> CodeEdit:
	return EditorInterface.get_script_editor().get_current_editor().get_base_editor() as CodeEdit

Probably a brazen and daring request for help

I really wanted to start my journey to Godot “right”, and therefore I immediately thought about a linter. The installation was successful (sort of). If you run the linting command from a third-party code editor (vs code), the result is working, but, unfortunately, the gdlint window says that the path to gdlint was not found.

I was looking for a solution to this problem, but it feels like I'm the only one who encountered this

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.