aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2025-10-03 22:41:14 +0200
committerDanielGavin <danielgavin5@hotmail.com>2025-10-03 22:41:14 +0200
commitf252aad41d51df05abab2a00490d8e7f16bc488d (patch)
treede2f3012a26aa0d6810f59a7a608a90d21b3b7d0 /src
parentfe88eb50b8d695a8f05e07846408751e56b054ee (diff)
Added diagnostic system to collect all the diagnostics. Unused imports are shown on save and open.
Diffstat (limited to 'src')
-rw-r--r--src/server/action.odin2
-rw-r--r--src/server/check.odin89
-rw-r--r--src/server/diagnostics.odin126
-rw-r--r--src/server/documents.odin52
-rw-r--r--src/server/requests.odin14
5 files changed, 181 insertions, 102 deletions
diff --git a/src/server/action.odin b/src/server/action.odin
index 058ea21..d1608b5 100644
--- a/src/server/action.odin
+++ b/src/server/action.odin
@@ -108,8 +108,6 @@ remove_unused_imports :: proc(
append(&textEdits, import_edit)
}
- log.error(textEdits[:])
-
workspaceEdit: WorkspaceEdit
workspaceEdit.changes = make(map[string][]TextEdit, 0, context.temp_allocator)
workspaceEdit.changes[uri] = textEdits[:]
diff --git a/src/server/check.odin b/src/server/check.odin
index b8f0c7c..5b8f2a1 100644
--- a/src/server/check.odin
+++ b/src/server/check.odin
@@ -19,9 +19,6 @@ import "core:thread"
import "src:common"
-//Store uris we have reported on since last save. We use this to clear them on next save.
-uris_reported: [dynamic]string
-
Json_Error :: struct {
type: string,
pos: Json_Type_Error,
@@ -68,7 +65,33 @@ fallback_find_odin_directories :: proc(config: ^common.Config) -> []string {
return data[:]
}
-check :: proc(paths: []string, uri: common.Uri, writer: ^Writer, config: ^common.Config) {
+check_unused_imports :: proc(document: ^Document, config: ^common.Config) {
+ if !config.enable_unused_imports_reporting {
+ return
+ }
+
+ diagnostics := make([dynamic]Diagnostic, context.temp_allocator)
+
+ unused_imports := find_unused_imports(document, context.temp_allocator)
+
+ remove_diagnostics(.Unused, document.uri.uri)
+
+ for imp in unused_imports {
+ add_diagnostics(
+ .Unused,
+ document.uri.uri,
+ Diagnostic {
+ range = common.get_token_range(imp.import_decl, document.ast.src),
+ severity = DiagnosticSeverity.Hint,
+ code = "Unused",
+ message = "unused import",
+ tags = {.Unnecessary},
+ },
+ )
+ }
+}
+
+check :: proc(paths: []string, uri: common.Uri, config: ^common.Config) {
paths := paths
if len(paths) == 0 {
@@ -138,6 +161,8 @@ check :: proc(paths: []string, uri: common.Uri, writer: ^Writer, config: ^common
log.errorf("Failed to unmarshal check results: %v, %v", res, string(buffer))
}
+ clear_diagnostics(.Check)
+
for error in json_errors.errors {
if len(error.msgs) == 0 {
break
@@ -149,12 +174,11 @@ check :: proc(paths: []string, uri: common.Uri, writer: ^Writer, config: ^common
continue
}
- if error.pos.file not_in errors {
- errors[error.pos.file] = make([dynamic]Diagnostic, context.temp_allocator)
- }
+ uri := common.create_uri(error.pos.file, context.temp_allocator)
- append(
- &errors[error.pos.file],
+ add_diagnostics(
+ .Check,
+ uri.uri,
Diagnostic {
code = "checker",
severity = .Error,
@@ -168,51 +192,4 @@ check :: proc(paths: []string, uri: common.Uri, writer: ^Writer, config: ^common
)
}
}
-
- for uri in uris_reported {
- params := NotificationPublishDiagnosticsParams {
- uri = uri,
- diagnostics = {},
- }
-
- notification := Notification {
- jsonrpc = "2.0",
- method = "textDocument/publishDiagnostics",
- params = params,
- }
-
- if writer != nil {
- send_notification(notification, writer)
- }
-
- delete(uri)
- }
-
- clear(&uris_reported)
-
- for k, v in errors {
- uri := common.create_uri(k, context.temp_allocator)
-
- //Find the unique diagnostics, since some poor profile settings make the checker check the same file multiple times
- unique := slice.unique(v[:])
-
- params := NotificationPublishDiagnosticsParams {
- uri = uri.uri,
- diagnostics = unique,
- }
-
- notifaction := Notification {
- jsonrpc = "2.0",
- method = "textDocument/publishDiagnostics",
- params = params,
- }
-
- append(&uris_reported, strings.clone(uri.uri))
-
- if writer != nil {
- send_notification(notifaction, writer)
- }
- }
-
-
}
diff --git a/src/server/diagnostics.odin b/src/server/diagnostics.odin
new file mode 100644
index 0000000..411ffbc
--- /dev/null
+++ b/src/server/diagnostics.odin
@@ -0,0 +1,126 @@
+package server
+
+import "core:log"
+import "core:slice"
+import "core:strings"
+import "src:common"
+
+DiagnosticType :: enum {
+ Syntax,
+ Unused,
+ Check,
+}
+
+diagnostics: [DiagnosticType]map[string][dynamic]Diagnostic
+
+add_diagnostics :: proc(type: DiagnosticType, uri: string, diagnostic: Diagnostic) {
+ diagnostic_type := &diagnostics[type]
+
+ if diagnostic_type == nil {
+ log.errorf("Diagnostic type did not exist: %v", type)
+ return
+ }
+
+ uri := uri
+
+ when ODIN_OS == .Windows {
+ uri = strings.to_lower(uri, context.temp_allocator)
+ }
+
+ diagnostic_array := &diagnostic_type[uri]
+
+ if diagnostic_array == nil {
+ diagnostic_type[strings.clone(uri)] = make([dynamic]Diagnostic)
+ diagnostic_array = &diagnostic_type[uri]
+ }
+
+ diagnostic := diagnostic
+
+ diagnostic.message = strings.clone(diagnostic.message)
+ diagnostic.code = strings.clone(diagnostic.code)
+
+ append(diagnostic_array, diagnostic)
+}
+
+remove_diagnostics :: proc(type: DiagnosticType, uri: string) {
+ diagnostic_type := &diagnostics[type]
+
+ if diagnostic_type == nil {
+ log.errorf("Diagnostic type did not exist: %v", type)
+ return
+ }
+
+ uri := uri
+
+ when ODIN_OS == .Windows {
+ uri = strings.to_lower(uri, context.temp_allocator)
+ }
+
+ diagnostic_array := &diagnostic_type[uri]
+
+ if diagnostic_array == nil {
+ return
+ }
+
+ for diagnostic in diagnostic_array {
+ delete(diagnostic.message)
+ delete(diagnostic.code)
+ }
+
+ clear(diagnostic_array)
+}
+
+clear_diagnostics :: proc(type: DiagnosticType) {
+ diagnostic_type := &diagnostics[type]
+
+ if diagnostic_type == nil {
+ log.errorf("Diagnostic type did not exist: %v", type)
+ return
+ }
+
+ for _, &diagnostic_array in diagnostic_type {
+ for diagnostic in diagnostic_array {
+ delete(diagnostic.message)
+ delete(diagnostic.code)
+ }
+ clear(&diagnostic_array)
+ }
+}
+
+push_diagnostics :: proc(writer: ^Writer) {
+ merged_diagnostics := make(map[string][dynamic]Diagnostic, context.temp_allocator)
+
+ for diagnostic_type in diagnostics {
+ for k, v in diagnostic_type {
+ diagnostic_array := &merged_diagnostics[k]
+
+ if diagnostic_array == nil {
+ merged_diagnostics[k] = make([dynamic]Diagnostic, context.temp_allocator)
+ diagnostic_array = &merged_diagnostics[k]
+ }
+
+ append(diagnostic_array, ..v[:])
+ }
+ }
+
+ for k, v in merged_diagnostics {
+ //Find the unique diagnostics, since some poor profile settings make the checker check the same file multiple times
+ unique := slice.unique(v[:])
+
+ params := NotificationPublishDiagnosticsParams {
+ uri = k,
+ diagnostics = unique,
+ }
+
+ notifaction := Notification {
+ jsonrpc = "2.0",
+ method = "textDocument/publishDiagnostics",
+ params = params,
+ }
+
+ if writer != nil {
+ send_notification(notifaction, writer)
+ }
+ }
+
+}
diff --git a/src/server/documents.odin b/src/server/documents.odin
index 54d995d..70584fd 100644
--- a/src/server/documents.odin
+++ b/src/server/documents.odin
@@ -320,18 +320,16 @@ document_refresh :: proc(document: ^Document, config: ^common.Config, writer: ^W
return .None
}
+ remove_diagnostics(.Syntax, document.uri.uri)
+ remove_diagnostics(.Check, document.uri.uri)
+
if writer != nil && !config.disable_parser_errors {
document.diagnosed_errors = true
- diagnostics := make([dynamic]Diagnostic, 0, len(errors), context.temp_allocator)
-
- params := NotificationPublishDiagnosticsParams {
- uri = document.uri.uri,
- }
-
for error, i in errors {
- append(
- &diagnostics,
+ add_diagnostics(
+ .Syntax,
+ document.uri.uri,
Diagnostic {
range = common.Range {
start = common.Position{line = error.line - 1, character = 0},
@@ -344,33 +342,7 @@ document_refresh :: proc(document: ^Document, config: ^common.Config, writer: ^W
)
}
- if config.enable_unused_imports_reporting {
- unused_imports := find_unused_imports(document, context.temp_allocator)
-
- for imp in unused_imports {
- append(
- &diagnostics,
- Diagnostic {
- range = common.get_token_range(imp.import_decl, document.ast.src),
- severity = DiagnosticSeverity.Hint,
- code = "Unused",
- message = "unused import",
- tags = {.Unnecessary},
- },
- )
- }
-
- }
-
- params.diagnostics = diagnostics[:]
-
- notifaction := Notification {
- jsonrpc = "2.0",
- method = "textDocument/publishDiagnostics",
- params = params,
- }
-
- send_notification(notifaction, writer)
+ push_diagnostics(writer)
}
return .None
@@ -507,18 +479,12 @@ get_import_range :: proc(imp: ^ast.Import_Decl, src: string) -> common.Range {
start := common.token_pos_to_position(imp.name.pos, src)
end := start
end.character += len(imp.name.text)
- return {
- start = start,
- end = end,
- }
+ return {start = start, end = end}
}
start := common.token_pos_to_position(imp.relpath.pos, src)
end := start
text_len := len(imp.relpath.text)
end.character += text_len
- return {
- start = start,
- end = end,
- }
+ return {start = start, end = end}
}
diff --git a/src/server/requests.odin b/src/server/requests.odin
index e153503..49119aa 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -1041,6 +1041,12 @@ notification_did_open :: proc(
return .InternalError
}
+ document := document_get(open_params.textDocument.uri)
+
+ check_unused_imports(document, config)
+
+ push_diagnostics(writer)
+
return .None
}
@@ -1135,7 +1141,13 @@ notification_did_save :: proc(
corrected_uri := common.create_uri(fullpath, context.temp_allocator)
- check(config.profile.checker_path[:], corrected_uri, writer, config)
+ check(config.profile.checker_path[:], corrected_uri, config)
+
+ document := document_get(save_params.textDocument.uri)
+
+ check_unused_imports(document, config)
+
+ push_diagnostics(writer)
return .None
}