Skip to content

Commit

Permalink
Refactored some global state handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Oct 12, 2024
1 parent c027d9c commit c68a377
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 23 deletions.
23 changes: 22 additions & 1 deletion src/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import text/[custom_treesitter]
import finder/[finder, previewer]
import compilation_config, vfs
import vcs/vcs
import service

import nimsumtree/[buffer, clock, rope]

Expand Down Expand Up @@ -139,6 +140,8 @@ type
fs*: Filesystem
vfs*: VFS

services: Services

logNextFrameTime*: bool = false
disableLogFrameTime*: bool = true
logBuffer = ""
Expand Down Expand Up @@ -284,6 +287,9 @@ proc addCommandScript*(self: App, context: string, subContext: string, keys: str
proc currentEventHandlers*(self: App): seq[EventHandler]
proc getEditorsForDocument(self: App, document: Document): seq[DocumentEditor]
proc showEditor*(self: App, editorId: EditorId, viewIndex: Option[int] = int.none)
proc getServices*(self: App): Services
proc getService*(self: App, name: string): Option[Service]
proc addService*(self: App, name: string, service: Service)

proc createView(self: App, editorState: OpenEditor): View

Expand Down Expand Up @@ -325,6 +331,9 @@ implTrait AppInterface, App:
getDocument(Option[Document], App, string, bool)
getOrOpenDocument(Option[Document], App, string, bool, bool)
tryCloseDocument(bool, App, Document, bool)
getServices(Services, App)
getService(Option[Service], App, string)
addService(void, App, string, Service)

type
AppLogger* = ref object of Logger
Expand Down Expand Up @@ -1154,7 +1163,7 @@ proc runLateCommandsFromAppOptions(self: App) =

proc finishInitialization*(self: App, state: EditorState): Future[void]

proc newApp*(backend: api.Backend, platform: Platform, fs: Filesystem, options = AppOptions()): Future[App] {.async.} =
proc newApp*(backend: api.Backend, platform: Platform, fs: Filesystem, services: Services, options = AppOptions()): Future[App] {.async.} =
var self = App()

{.gcsafe.}:
Expand All @@ -1170,6 +1179,7 @@ proc newApp*(backend: api.Backend, platform: Platform, fs: Filesystem, options =
self.backend = backend
self.statusBarOnTop = false
self.appOptions = options
self.services = services

self.vfs = VFS()
self.vfs.mount("", VFSNull())
Expand Down Expand Up @@ -1706,6 +1716,8 @@ proc setWorkspaceFolder(self: App, workspace: Workspace): Future[bool] {.async.}
self.workspace = workspace
self.vfs.mount "", VFSWorkspace(workspace: workspace)

self.services.getService(WorkspaceService).get.workspace = workspace

{.gcsafe.}:
if gWorkspace.isNil:
setGlobalWorkspace(workspace)
Expand Down Expand Up @@ -2149,6 +2161,15 @@ proc tryCloseDocument*(self: App, document: Document, force: bool): bool =

return true

proc getServices*(self: App): Services =
self.services

proc getService*(self: App, name: string): Option[Service] =
self.services.getService(name)

proc addService*(self: App, name: string, service: Service) =
self.services.addService(name, service)

proc moveCurrentViewToTop*(self: App) {.expose("editor").} =
if self.views.len > 0:
let view = self.views[self.currentView]
Expand Down
16 changes: 15 additions & 1 deletion src/app_interface.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import platform/platform
import finder/[finder, previewer]
import events, popup, document_editor, document, config_provider, selector_popup_builder, register
from scripting_api import EditorId
import service
export service

traitRef AppInterface:
method platform*(self: AppInterface): Platform {.gcsafe, raises: [].}
Expand Down Expand Up @@ -40,5 +42,17 @@ traitRef AppInterface:
method tryCloseDocument*(self: AppInterface, document: Document, force: bool): bool {.gcsafe, raises: [].}
method onEditorRegisteredEvent*(self: AppInterface): ptr Event[DocumentEditor] {.gcsafe, raises: [].}
method onEditorDeregisteredEvent*(self: AppInterface): ptr Event[DocumentEditor] {.gcsafe, raises: [].}
method getServices*(self: AppInterface): Services {.gcsafe, raises: [].}
method getService*(self: AppInterface, name: string): Option[Service] {.gcsafe, raises: [].}
method addService*(self: AppInterface, name: string, service: Service) {.gcsafe, raises: [].}

var gAppInterface*: AppInterface = nil
proc getService*(self: AppInterface, T: typedesc[Service]): Option[T] {.gcsafe, raises: [].} =
let service = self.getService(T.serviceName)
if service.isSome and service.get of T:
service.get.T.some
return T.none

proc addService*[T: Service](self: AppInterface, service: T) {.gcsafe, raises: [].} =
self.addService(T.serviceName, service)

var gAppInterface*: AppInterface = nil
6 changes: 5 additions & 1 deletion src/desktop_main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,12 @@ proc run(app: App, rend: Platform, backend: Backend) =
{.gcsafe.}:
logger.flush()

import service
var services = Services()
services.addBuiltinServices()

proc main() =
let app = waitFor newApp(backend.get, rend, fs, opts)
let app = waitFor newApp(backend.get, rend, fs, services, opts)
run(app, rend, backend.get)

try:
Expand Down
4 changes: 2 additions & 2 deletions src/document_editor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ method handleMouseMove*(self: DocumentEditor, mousePosWindow: Vec2, mousePosDelt
discard

method getStateJson*(self: DocumentEditor): JsonNode {.base, gcsafe, raises: [].} =
discard
return newJObject()

method restoreStateJson*(self: DocumentEditor, state: JsonNode) {.base, gcsafe, raises: [].} =
discard
Expand All @@ -104,4 +104,4 @@ method getStatisticsString*(self: DocumentEditor): string {.base, gcsafe, raises

import app_interface
method injectDependencies*(self: DocumentEditor, ed: AppInterface, fs: Filesystem) {.base, gcsafe, raises: [].} =
discard
discard
7 changes: 5 additions & 2 deletions src/misc/custom_async.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import std/[threadpool]
type NoExceptionDestroy* = object
func `=destroy`*(x: NoExceptionDestroy) {.raises: [].} = discard

import chronos
export chronos
import chronos except asyncDiscard
export chronos except asyncDiscard

proc runAsyncVoid[A](f: proc(options: A) {.gcsafe, raises: [].}, options: A): NoExceptionDestroy {.raises: [].} =
f(options)
Expand Down Expand Up @@ -67,6 +67,9 @@ proc doneFuture*(): Future[void] =
except:
assert false

proc asyncDiscard*[T](f: Future[T]): Future[void] {.async.} =
discard await f

template readFinished*[T: not void](fut: Future[T]): lent T =
try:
fut.read
Expand Down
6 changes: 5 additions & 1 deletion src/misc/custom_logger.nim
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,11 @@ template logCategory*(category: static string, noDebug = false): untyped =
body
block:
let descriptionString = description
logging.log(lvlInfo, "[" & category & "] " & descriptionString & " took " & $timer.elapsed.ms & " ms")
try:
{.gcsafe.}:
logging.log(lvlInfo, "[" & category & "] " & descriptionString & " took " & $timer.elapsed.ms & " ms")
except:
discard

template logScope(level: logging.Level, text: string): untyped {.used.} =
let txt = text
Expand Down
2 changes: 1 addition & 1 deletion src/misc/util.nim
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ template with*(exp, val, body: untyped): untyped =
template catch*(exp: untyped, then: untyped): untyped =
try:
exp
except CatchableError:
except Exception:
then

template catch*(exp: untyped, error: untyped, then: untyped): untyped =
Expand Down
2 changes: 0 additions & 2 deletions src/scripting/expose.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


import std/[json, strutils, sequtils, tables, options, macros, genasts, macrocache, typetraits, sugar]
from std/logging import nil
import fusion/matching
Expand Down
130 changes: 130 additions & 0 deletions src/service.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import std/[json, strutils, sequtils, tables, options, macros, genasts, macrocache, typetraits, sugar]
import misc/[util, custom_logger, custom_async]

const builtinServices = CacheSeq"builtinServices"

{.push gcsafe.}
{.push raises: [].}

logCategory "services"

type
ServiceState* = enum
Pending
Failed
Registered
Running

Service* = ref object of RootObj
state*: ServiceState
services*: Services

Services* = ref object
running: Table[string, Service]
registered: Table[string, Service]
dependencies: Table[string, seq[string]]
pending: Table[string, Future[Option[Service]]]

method init*(self: Service): Future[Result[void, ref CatchableError]] {.base, async: (raises: []).} = discard

proc getService*(self: Services, name: string, state: Option[ServiceState] = ServiceState.none): Option[Service] =
if state.get(Running) == Running:
self.running.withValue(name, service):
return service[].some
if state.get(Registered) == Registered:
self.registered.withValue(name, service):
return service[].some
Service.none

proc initService(self: Services, name: string) {.async.} =
log lvlInfo, &"initService {name}"
let service = self.registered[name]
let res = await service.init()
if res.isOk:
self.running[name] = service
service.state = Running
self.pending.withValue(name, fut):
fut[].complete(service.some)
self.pending.del(name)

for (s, deps) in self.dependencies.mpairs:
let idx = deps.find(name)
if idx == -1:
continue

deps.removeSwap(idx)
if deps.len == 0:
await self.initService(s)
break

else:
log lvlError, &"Failed to initialize service '{name}'"
service.state = Failed

proc addService*(self: Services, name: string, service: Service, dependencies: seq[string] = @[]) =
log lvlInfo, &"addService {name}"
service.services = self
self.registered[name] = service
service.state = Registered

if dependencies.len == 0:
asyncSpawn self.initService(name)
else:
self.dependencies[name] = dependencies

proc getService*(self: Services, T: typedesc): Option[T] {.gcsafe, raises: [].} =
let service = self.getService(T.serviceName)
if service.isSome and service.get of T:
return service.get.T.some
return T.none

proc getServiceAsync*(self: Services, T: typedesc): Future[Option[T]] {.gcsafe, async: (raises: []).} =
var service = self.getService(T.serviceName)
if service.isSome and service.get of T:
return service.get.T.some
self.pending.withValue(T.serviceName, fut):
try:
service = fut[].await
except CatchableError as e:
log lvlError, &"Failed to await service {T.serviceName}: {e.msg}\n{e.getStackTrace()}"
return T.none

if service.isSome and service.get of T:
return service.get.T.some
return T.none

proc addService*[T: Service](self: Services, service: T) {.gcsafe, raises: [].} =
self.addService(T.serviceName, service)

# func addBuiltinServiceImpl(T: NimNode, name: static string, names: static seq[string], dependencies: varargs[typed]) =
# echo "addBuiltinService ", T.treeRepr, ", ", dependencies.treeRepr
# builtinServices.add nnkTupleExpr.newTree(T, genAst(T, T.serviceName))

macro addBuiltinServiceImpl(T: typed, name: static string, names: static seq[string], dependencies: varargs[typed]) =
echo "addBuiltinService ", T.treeRepr, ", ", dependencies.treeRepr
var dependencyNames = nnkBracket.newTree()
for n in names:
dependencyNames.add n.newLit

builtinServices.add nnkBracket.newTree(T, newLit(name), nnkPrefix.newTree(ident"@", dependencyNames))

macro addBuiltinService*(T: typed, dependencies: varargs[typed]) =
echo "addBuiltinService ", T.treeRepr, ", ", dependencies.treeRepr
var dependencyNames = nnkPrefix.newTree(ident"@", nnkBracket.newTree())
for d in dependencies:
dependencyNames.add genAst(d, d.serviceName)
echo dependencyNames.treeRepr
result = genAst(T, dependencies):
addBuiltinServiceImpl(T, T.serviceName, @[], dependencies)

macro addBuiltinServices*(services: Services) =
result = nnkStmtList.newTree()

for serviceInfo in builtinServices:
let T = serviceInfo[0]
let name = serviceInfo[1]
let dependencies = serviceInfo[2]
echo serviceInfo.treeRepr
result.add genAst(services, T, name, dependencies, services.addService(name, T(), dependencies))

echo result.repr
16 changes: 8 additions & 8 deletions src/ui/node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1303,37 +1303,37 @@ macro panel*(builder: UINodeBuilder, inFlags: UINodeFlags, args: varargs[untyped
let currentNode {.used, inject.} = node

template onClick(button: MouseButton, onClickBody: untyped) {.used.} =
currentNode.handlePressed = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool =
currentNode.handlePressed = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
if btn == button:
onClickBody

template onClickAny(btn: untyped, onClickBody: untyped) {.used.} =
currentNode.handlePressed = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool =
currentNode.handlePressed = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
onClickBody

template onReleased(onBody: untyped) {.used.} =
currentNode.handleReleased = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool =
currentNode.handleReleased = proc(node {.inject.}: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
onBody

template onDrag(button: MouseButton, onDragBody: untyped) {.used.} =
currentNode.handleDrag = proc(node: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2, d: Vec2): bool =
currentNode.handleDrag = proc(node: UINode, btn {.inject.}: MouseButton, modifiers {.inject.}: set[Modifier], pos {.inject.}: Vec2, d: Vec2): bool {.gcsafe, raises: [].} =
if btn == button:
onDragBody

template onBeginHover(onBody: untyped) {.used.} =
currentNode.handleBeginHover = proc(node: UINode, pos {.inject.}: Vec2): bool =
currentNode.handleBeginHover = proc(node: UINode, pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
onBody

template onEndHover(onBody: untyped) {.used.} =
currentNode.handleEndHover = proc(node: UINode, pos {.inject.}: Vec2): bool =
currentNode.handleEndHover = proc(node: UINode, pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
onBody

template onHover(onBody: untyped) {.used.} =
currentNode.handleHover = proc(node: UINode, pos {.inject.}: Vec2): bool =
currentNode.handleHover = proc(node: UINode, pos {.inject.}: Vec2): bool {.gcsafe, raises: [].} =
onBody

template onScroll(onBody: untyped) {.used.} =
currentNode.handleScroll = proc(node: UINode, pos {.inject.}: Vec2, delta {.inject.}: Vec2, modifiers {.inject.}: set[Modifier]): bool =
currentNode.handleScroll = proc(node: UINode, pos {.inject.}: Vec2, delta {.inject.}: Vec2, modifiers {.inject.}: set[Modifier]): bool {.gcsafe, raises: [].} =
onBody
return true

Expand Down
3 changes: 0 additions & 3 deletions src/ui/widget_builder_text_document.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1110,9 +1110,6 @@ proc createCompletions(self: TextDocumentEditor, builder: UINodeBuilder, app: Ap
if completionsPanel.bounds.xw > completionsPanel.parent.bounds.w:
completionsPanel.rawX = max(completionsPanel.parent.bounds.w - completionsPanel.bounds.w, 0)

method createUI*(self: EditorView, builder: UINodeBuilder, app: App): seq[OverlayFunction] =
self.editor.createUI(builder, app)

method createUI*(self: TextDocumentEditor, builder: UINodeBuilder, app: App): seq[OverlayFunction] =
self.preRender()

Expand Down
3 changes: 3 additions & 0 deletions src/ui/widget_builders.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ when enableAst:

logCategory "widget_builder"

method createUI*(self: EditorView, builder: UINodeBuilder, app: App): seq[OverlayFunction] =
self.editor.createUI(builder, app)

proc updateWidgetTree*(self: App, frameIndex: int) =
# self.platform.builder.buildUINodes()

Expand Down
Loading

0 comments on commit c68a377

Please sign in to comment.