diff options
Diffstat (limited to 'lib/js')
-rw-r--r-- | lib/js/asyncjs.nim | 37 | ||||
-rw-r--r-- | lib/js/dom.nim | 166 | ||||
-rw-r--r-- | lib/js/jscore.nim | 26 | ||||
-rw-r--r-- | lib/js/jsffi.nim | 124 | ||||
-rw-r--r-- | lib/js/jsre.nim | 7 |
5 files changed, 200 insertions, 160 deletions
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 8f91e1acd..9b043f3e5 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -17,37 +17,42 @@ ## ## This is roughly equivalent to the `async` keyword in JavaScript code. ## -## .. code-block:: nim -## proc loadGame(name: string): Future[Game] {.async.} = -## # code +## ```nim +## proc loadGame(name: string): Future[Game] {.async.} = +## # code +## ``` ## ## should be equivalent to ## -## .. code-block:: javascript +## ```javascript ## async function loadGame(name) { ## // code ## } +## ``` ## ## A call to an asynchronous procedure usually needs `await` to wait for ## the completion of the `Future`. ## -## .. code-block:: nim +## ```nim ## var game = await loadGame(name) +## ``` ## ## Often, you might work with callback-based API-s. You can wrap them with ## asynchronous procedures using promises and `newPromise`: ## -## .. code-block:: nim +## ```nim ## proc loadGame(name: string): Future[Game] = ## var promise = newPromise() do (resolve: proc(response: Game)): ## cbBasedLoadGame(name) do (game: Game): ## resolve(game) ## return promise +## ``` ## ## Forward definitions work properly, you just need to always add the `{.async.}` pragma: ## -## .. code-block:: nim +## ```nim ## proc loadGame(name: string): Future[Game] {.async.} +## ``` ## ## JavaScript compatibility ## ======================== @@ -57,7 +62,7 @@ ## If you need to use this module with older versions of JavaScript, you can ## use a tool that backports the resulting JavaScript code, as babel. -# xxx code-block:: javascript above gives `LanguageXNotSupported` warning. +# xxx code: javascript above gives `LanguageXNotSupported` warning. when not defined(js) and not defined(nimsuggest): {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".} @@ -85,6 +90,8 @@ proc replaceReturn(node: var NimNode) = node[z] = nnkReturnStmt.newTree(value) elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result": node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(jsResolve, son[1])) + elif son.kind in RoutineNodes: + discard else: replaceReturn(son) inc z @@ -95,10 +102,18 @@ proc isFutureVoid(node: NimNode): bool = node[1].kind == nnkIdent and $node[1] == "void" proc generateJsasync(arg: NimNode): NimNode = - if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: + if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo, nnkProcTy}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.") + # Transform type X = proc (): something {.async.} + # into type X = proc (): Future[something] + if arg.kind == nnkProcTy: + result = arg + if arg[0][0].kind == nnkEmpty: + result[0][0] = quote do: Future[void] + return result + result = arg var isVoid = false let jsResolve = ident("jsResolve") @@ -228,7 +243,7 @@ since (1, 5, 1): else: type A = impl(onSuccess(default(T))) var ret: A - asm "`ret` = `future`.then(`onSuccess`, `onReject`)" + {.emit: "`ret` = `future`.then(`onSuccess`, `onReject`);".} return ret proc catch*[T](future: Future[T], onReject: OnReject): Future[void] = @@ -251,4 +266,4 @@ since (1, 5, 1): discard main() - asm "`result` = `future`.catch(`onReject`)" + {.emit: "`result` = `future`.catch(`onReject`);".} diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 9859e95ae..be2a34db1 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -9,6 +9,37 @@ ## Declaration of the Document Object Model for the `JavaScript backend ## <backends.html#backends-the-javascript-target>`_. +## +## +## Document Ready +## -------------- +## +## * Basic example of a document ready: +runnableExamples"-b:js -r:off": + proc example(e: Event) = echo "Document is ready" + document.addEventListener("DOMContentLoaded", example) # You can also use "load" event. +## * This example runs 5 seconds after the document ready: +runnableExamples"-b:js -r:off": + proc example() = echo "5 seconds after document ready" + proc domReady(e: Event) = discard setTimeout(example, 5_000) # Document is ready. + document.addEventListener("DOMContentLoaded", domReady) +## Document onUnload +## ----------------- +## +## * Simple example of how to implement code that runs when the page unloads: +runnableExamples"-b:js -r:off": + proc example(e: Event) = echo "Document is unloaded" + document.addEventListener("unload", example) # You can also use "beforeunload". +## Document Autorefresh +## -------------------- +## +## * Minimal example of a document autorefresh: +runnableExamples"-b:js -r:off": + proc example() = window.location.reload() + discard setTimeout(example, 5_000) +## - For more examples, see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + + import std/private/since when not defined(js): {.error: "This module only works on the JavaScript platform".} @@ -17,8 +48,7 @@ const DomApiVersion* = 3 ## the version of DOM API we try to follow. No guarantees though. type - EventTarget* = ref EventTargetObj - EventTargetObj {.importc.} = object of RootObj + EventTarget* {.importc.} = ref object of RootObj onabort*: proc (event: Event) {.closure.} onblur*: proc (event: Event) {.closure.} onchange*: proc (event: Event) {.closure.} @@ -129,8 +159,7 @@ type Storage* {.importc.} = ref object - Window* = ref WindowObj - WindowObj {.importc.} = object of EventTargetObj + Window* {.importc.} = ref object of EventTarget document*: Document event*: Event history*: History @@ -159,11 +188,9 @@ type sessionStorage*: Storage parent*: Window - Frame* = ref FrameObj - FrameObj {.importc.} = object of WindowObj + Frame* {.importc.} = ref object of Window - ClassList* = ref ClassListObj - ClassListObj {.importc.} = object of RootObj + ClassList* {.importc.} = ref object of RootObj NodeType* = enum ElementNode = 1, @@ -179,8 +206,7 @@ type DocumentFragmentNode, NotationNode - Node* = ref NodeObj - NodeObj {.importc.} = object of EventTargetObj + Node* {.importc.} = ref object of EventTarget attributes*: seq[Node] childNodes*: seq[Node] children*: seq[Node] @@ -204,8 +230,7 @@ type parentElement*: Element isConnected*: bool - Document* = ref DocumentObj - DocumentObj {.importc.} = object of NodeObj + Document* {.importc.} = ref object of Node activeElement*: Element documentElement*: Element alinkColor*: cstring @@ -232,8 +257,7 @@ type links*: seq[LinkElement] fonts*: FontFaceSet - Element* = ref ElementObj - ElementObj {.importc.} = object of NodeObj + Element* {.importc.} = ref object of Node className*: cstring classList*: ClassList checked*: bool @@ -254,8 +278,7 @@ type offsetLeft*: int offsetTop*: int - ValidityState* = ref ValidityStateObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ValidityState>`_ - ValidityStateObj {.importc.} = object + ValidityState* {.importc.} = ref object ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ValidityState>`_ badInput*: bool customError*: bool patternMismatch*: bool @@ -268,25 +291,21 @@ type valid*: bool valueMissing*: bool - Blob* = ref BlobObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Blob>`_ - BlobObj {.importc.} = object of RootObj + Blob* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Blob>`_ size*: int `type`*: cstring - File* = ref FileObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/File>`_ - FileObj {.importc.} = object of Blob + File* {.importc.} = ref object of Blob ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/File>`_ lastModified*: int name*: cstring - TextAreaElement* = ref TextAreaElementObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement>`_ - TextAreaElementObj {.importc.} = object of Element + TextAreaElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement>`_ value*: cstring selectionStart*, selectionEnd*: int selectionDirection*: cstring rows*, cols*: int - InputElement* = ref InputElementObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement>`_ - InputElementObj {.importc.} = object of Element + InputElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement>`_ # Properties related to the parent form formAction*: cstring formEncType*: cstring @@ -338,15 +357,13 @@ type valueAsDate*: cstring valueAsNumber*: float - LinkElement* = ref LinkObj - LinkObj {.importc.} = object of ElementObj + LinkElement* {.importc.} = ref object of Element target*: cstring text*: cstring x*: int y*: int - EmbedElement* = ref EmbedObj - EmbedObj {.importc.} = object of ElementObj + EmbedElement* {.importc.} = ref object of Element height*: int hspace*: int src*: cstring @@ -354,21 +371,18 @@ type `type`*: cstring vspace*: int - AnchorElement* = ref AnchorObj - AnchorObj {.importc.} = object of ElementObj + AnchorElement* {.importc.} = ref object of Element text*: cstring x*, y*: int - OptionElement* = ref OptionObj - OptionObj {.importc.} = object of ElementObj + OptionElement* {.importc.} = ref object of Element defaultSelected*: bool selected*: bool selectedIndex*: int text*: cstring value*: cstring - FormElement* = ref FormObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement>`_ - FormObj {.importc.} = object of ElementObj + FormElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement>`_ acceptCharset*: cstring action*: cstring autocomplete*: cstring @@ -380,8 +394,7 @@ type noValidate*: bool target*: cstring - ImageElement* = ref ImageObj - ImageObj {.importc.} = object of ElementObj + ImageElement* {.importc.} = ref object of Element border*: int complete*: bool height*: int @@ -391,8 +404,7 @@ type vspace*: int width*: int - Style* = ref StyleObj - StyleObj {.importc.} = object of RootObj + Style* {.importc.} = ref object of RootObj alignContent*: cstring alignItems*: cstring alignSelf*: cstring @@ -768,8 +780,7 @@ type AtTarget, BubblingPhase - Event* = ref EventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Event>`_ - EventObj {.importc.} = object of RootObj + Event* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Event>`_ bubbles*: bool cancelBubble*: bool cancelable*: bool @@ -781,13 +792,11 @@ type `type`*: cstring isTrusted*: bool - UIEvent* = ref UIEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/UIEvent>`_ - UIEventObj {.importc.} = object of Event + UIEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/UIEvent>`_ detail*: int64 view*: Window - KeyboardEvent* = ref KeyboardEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent>`_ - KeyboardEventObj {.importc.} = object of UIEvent + KeyboardEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent>`_ altKey*, ctrlKey*, metaKey*, shiftKey*: bool code*: cstring isComposing*: bool @@ -1149,8 +1158,7 @@ type FourthButton = 8, FifthButton = 16 - MouseEvent* = ref MouseEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent>`_ - MouseEventObj {.importc.} = object of UIEvent + MouseEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent>`_ altKey*, ctrlKey*, metaKey*, shiftKey*: bool button*: int buttons*: int @@ -1167,13 +1175,11 @@ type File = "file", String = "string" - DataTransferItem* = ref DataTransferItemObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem>`_ - DataTransferItemObj {.importc.} = object of RootObj + DataTransferItem* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem>`_ kind*: cstring `type`*: cstring - DataTransfer* = ref DataTransferObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer>`_ - DataTransferObj {.importc.} = object of RootObj + DataTransfer* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer>`_ dropEffect*: cstring effectAllowed*: cstring files*: seq[Element] @@ -1215,8 +1221,7 @@ type ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent>`_ clipboardData*: DataTransfer - StorageEvent* = ref StorageEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent>`_ - StorageEventObj {.importc.} = object of Event + StorageEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent>`_ key*: cstring newValue*, oldValue*: cstring storageArea*: Storage @@ -1225,8 +1230,7 @@ type TouchList* {.importc.} = ref object of RootObj length*: int - Touch* = ref TouchObj - TouchObj {.importc.} = object of RootObj + Touch* {.importc.} = ref object of RootObj identifier*: int screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int target*: Element @@ -1234,12 +1238,10 @@ type rotationAngle*: int force*: float - TouchEvent* = ref TouchEventObj - TouchEventObj {.importc.} = object of UIEvent + TouchEvent* {.importc.} = ref object of UIEvent changedTouches*, targetTouches*, touches*: seq[Touch] - Location* = ref LocationObj - LocationObj {.importc.} = object of RootObj + Location* {.importc.} = ref object of RootObj hash*: cstring host*: cstring hostname*: cstring @@ -1250,12 +1252,10 @@ type search*: cstring origin*: cstring - History* = ref HistoryObj - HistoryObj {.importc.} = object of RootObj + History* {.importc.} = ref object of RootObj length*: int - Navigator* = ref NavigatorObj - NavigatorObj {.importc.} = object of RootObj + Navigator* {.importc.} = ref object of RootObj appCodeName*: cstring appName*: cstring appVersion*: cstring @@ -1293,8 +1293,7 @@ type ToolBar* = LocationBar StatusBar* = LocationBar - Screen = ref ScreenObj - ScreenObj {.importc.} = object of RootObj + Screen* {.importc.} = ref object of RootObj availHeight*: int availWidth*: int colorDepth*: int @@ -1324,8 +1323,7 @@ type `block`*: cstring inline*: cstring - MediaQueryList* = ref MediaQueryListObj - MediaQueryListObj {.importc.} = object of EventTargetObj + MediaQueryList* {.importc.} = ref object of EventTarget matches*: bool media*: cstring @@ -1335,35 +1333,31 @@ since (1, 3): ## DOM Parser object (defined on browser only, may not be on NodeJS). ## * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser ## - ## .. code-block:: nim + ## ```nim ## let prsr = newDomParser() ## discard prsr.parseFromString("<html><marquee>Hello World</marquee></html>".cstring, "text/html".cstring) + ## ``` - DomException* = ref DOMExceptionObj + DomException* {.importc.} = ref object ## The DOMException interface represents an abnormal event (called an exception) ## which occurs as a result of calling a method or accessing a property of a web API. ## Each exception has a name, which is a short "CamelCase" style string identifying ## the error or abnormal condition. ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException - DOMExceptionObj {.importc.} = object - - FileReader* = ref FileReaderObj + FileReader* {.importc.} = ref object of EventTarget ## The FileReader object lets web applications asynchronously read the contents of files ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify ## the file or data to read. ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader - FileReaderObj {.importc.} = object of EventTargetObj - FileReaderState* = distinct range[0'u16..2'u16] RootNodeOptions* = object of RootObj composed*: bool DocumentOrShadowRoot* {.importc.} = object of RootObj activeElement*: Element # styleSheets*: StyleSheetList - ShadowRoot* = ref ShadowRootObj - ShadowRootObj {.importc.} = object of DocumentOrShadowRoot + ShadowRoot* {.importc.} = ref object of DocumentOrShadowRoot delegatesFocus*: bool host*: Element innerHTML*: cstring @@ -1372,8 +1366,7 @@ since (1, 3): mode*: cstring delegatesFocus*: bool - HTMLSlotElement* = ref HTMLSlotElementObj - HTMLSlotElementObj {.importc.} = object of RootObj + HTMLSlotElement* {.importc.} = ref object of RootObj name*: cstring SlotOptions* = object of RootObj flatten*: bool @@ -1430,7 +1423,7 @@ when defined(nodejs): parent.childNodes[i] = newNode return inc i - doAssert false, "old node not in node list" + raiseAssert "old node not in node list" proc removeChild*(parent, child: Node) = child.parentNode = nil @@ -1440,7 +1433,7 @@ when defined(nodejs): parent.childNodes.delete(i) return inc i - doAssert false, "old node not in node list" + raiseAssert "old node not in node list" proc insertBefore*(parent, newNode, before: Node) = appendChild(parent, newNode) @@ -1452,7 +1445,7 @@ when defined(nodejs): parent.childNodes[i-1] = newNode return inc i - #doAssert false, "before not in node list" + #raiseAssert "before not in node list" proc createElement*(d: Document, identifier: cstring): Element = new(result) @@ -1510,7 +1503,7 @@ proc confirm*(w: Window, msg: cstring): bool proc disableExternalCapture*(w: Window) proc enableExternalCapture*(w: Window) proc find*(w: Window, text: cstring, caseSensitive = false, - backwards = false) + backwards = false): bool proc focus*(w: Window) proc forward*(w: Window) proc getComputedStyle*(w: Window, e: Node, pe: Node = nil): Style @@ -1662,7 +1655,7 @@ proc item*(list: TouchList, i: int): Touch proc clearData*(dt: DataTransfer, format: cstring) proc getData*(dt: DataTransfer, format: cstring): cstring proc setData*(dt: DataTransfer, format: cstring, data: cstring) -proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int64, yOffset: int64) +proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int, yOffset: int) # DataTransferItem "methods" proc getAsFile*(dti: DataTransferItem): File @@ -1689,7 +1682,6 @@ proc `$`*(s: Selection): string = $(s.toString()) # Storage "methods" proc getItem*(s: Storage, key: cstring): cstring proc setItem*(s: Storage, key, value: cstring) -proc hasItem*(s: Storage, key: cstring): bool proc clear*(s: Storage) proc removeItem*(s: Storage, key: cstring) @@ -1839,3 +1831,11 @@ since (1, 7): proc matches*(self: Node; cssSelector: cstring): bool {.importjs: "(#.$1(#) || false)".} ## https://developer.mozilla.org/en-US/docs/Web/API/Element/matches + + +since (2, 1): + type VisualViewport* {.importc.} = ref object of EventTarget + offsetLeft*, offsetTop*, pageLeft*, pageTop*, width*, height*, scale*: float + onResize*, onScroll*: proc (event: Event) {.closure.} + + func visualViewport*(self: Window): VisualViewport {.importjs: "#.$1", nodecl.} diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 781e8fd57..be353875c 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -13,7 +13,7 @@ ## specific requirements and solely targets JavaScript, you should be using ## the relevant functions in the `math`, `json`, and `times` stdlib ## modules instead. -import std/private/since +import std/private/[since, jsutils] when not defined(js): {.error: "This module only works on the JavaScript platform".} @@ -74,9 +74,16 @@ proc parse*(d: DateLib, s: cstring): int {.importcpp.} proc newDate*(): DateTime {. importcpp: "new Date()".} -proc newDate*(date: int|int64|string): DateTime {. +proc newDate*(date: int|string): DateTime {. importcpp: "new Date(#)".} +whenJsNoBigInt64: + proc newDate*(date: int64): DateTime {. + importcpp: "new Date(#)".} +do: + proc newDate*(date: int64): DateTime {. + importcpp: "new Date(Number(#))".} + proc newDate*(year, month, day, hours, minutes, seconds, milliseconds: int): DateTime {. importcpp: "new Date(#,#,#,#,#,#,#)".} @@ -88,20 +95,27 @@ proc getMilliseconds*(d: DateTime): int {.importcpp.} proc getMinutes*(d: DateTime): int {.importcpp.} proc getMonth*(d: DateTime): int {.importcpp.} proc getSeconds*(d: DateTime): int {.importcpp.} -proc getYear*(d: DateTime): int {.importcpp.} proc getTime*(d: DateTime): int {.importcpp.} -proc toString*(d: DateTime): cstring {.importcpp.} +proc getTimezoneOffset*(d: DateTime): int {.importcpp.} proc getUTCDate*(d: DateTime): int {.importcpp.} +proc getUTCDay*(d: DateTime): int {.importcpp.} proc getUTCFullYear*(d: DateTime): int {.importcpp.} proc getUTCHours*(d: DateTime): int {.importcpp.} proc getUTCMilliseconds*(d: DateTime): int {.importcpp.} proc getUTCMinutes*(d: DateTime): int {.importcpp.} proc getUTCMonth*(d: DateTime): int {.importcpp.} proc getUTCSeconds*(d: DateTime): int {.importcpp.} -proc getUTCDay*(d: DateTime): int {.importcpp.} -proc getTimezoneOffset*(d: DateTime): int {.importcpp.} +proc getYear*(d: DateTime): int {.importcpp.} + proc setFullYear*(d: DateTime, year: int) {.importcpp.} +func toDateString*(d: DateTime): cstring {.importcpp.} +func toISOString*(d: DateTime): cstring {.importcpp.} +func toJSON*(d: DateTime): cstring {.importcpp.} +proc toString*(d: DateTime): cstring {.importcpp.} +func toTimeString*(d: DateTime): cstring {.importcpp.} +func toUTCString*(d: DateTime): cstring {.importcpp.} + #JSON library proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.} proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.} diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index aca4fc292..d50d58ae5 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -227,36 +227,40 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = assert obj.a.to(int) == 20 if validJsName($field): let importString = "#." & $field + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`.} + `helperName`(`obj`) else: if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`.} + `helperName`(`obj`) macro `.=`*(obj: JsObject, field, value: untyped): untyped = ## Experimental dot accessor (set) for type JsObject. ## Sets the value of a property of name `field` in a JsObject `x` to `value`. if validJsName($field): let importString = "#." & $field & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject, v: auto) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: JsObject, v: auto) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) else: if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject, v: auto) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: JsObject, v: auto) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) macro `.()`*(obj: JsObject, field: untyped, @@ -268,15 +272,14 @@ macro `.()`*(obj: JsObject, ## so be careful when using this.) ## ## Example: - ## - ## .. code-block:: nim - ## - ## # Let's get back to the console example: - ## var console {.importc, nodecl.}: JsObject - ## let res = console.log("I return undefined!") - ## console.log(res) # This prints undefined, as console.log always returns - ## # undefined. Thus one has to be careful, when using - ## # JsObject calls. + ## ```nim + ## # Let's get back to the console example: + ## var console {.importc, nodecl.}: JsObject + ## let res = console.log("I return undefined!") + ## console.log(res) # This prints undefined, as console.log always returns + ## # undefined. Thus one has to be careful, when using + ## # JsObject calls. + ## ``` var importString: string if validJsName($field): importString = "#." & $field & "(@)" @@ -284,10 +287,11 @@ macro `.()`*(obj: JsObject, if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & "(@)" - result = quote: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym, discardable.} - helper(`obj`) + let helperName = genSym(nskProc, "helper") + result = quote do: + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`, discardable.} + `helperName`(`obj`) for idx in 0 ..< args.len: let paramName = newIdentNode("param" & $idx) result[0][3].add newIdentDefs(paramName, newIdentNode("JsObject")) @@ -304,10 +308,11 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V], if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: type(`obj`)): `obj`.V - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: type(`obj`)): `obj`.V + {.importjs: `importString`.} + `helperName`(`obj`) macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, @@ -321,10 +326,11 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: type(`obj`), v: `obj`.V) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: type(`obj`), v: `obj`.V) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, @@ -407,21 +413,20 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto = ## ## Example: ## - ## .. code-block:: nim + ## ```nim + ## # Let's say we have a type with a ton of fields, where some fields do not + ## # need to be set, and we do not want those fields to be set to `nil`: + ## type + ## ExtremelyHugeType = ref object + ## a, b, c, d, e, f, g: int + ## h, i, j, k, l: cstring + ## # And even more fields ... ## - ## # Let's say we have a type with a ton of fields, where some fields do not - ## # need to be set, and we do not want those fields to be set to `nil`: - ## type - ## ExtremelyHugeType = ref object - ## a, b, c, d, e, f, g: int - ## h, i, j, k, l: cstring - ## # And even more fields ... - ## - ## let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 } - ## - ## # This generates roughly the same JavaScript as: - ## {.emit: "var obj = {a: 1, k: "foo", d: 42};".} + ## let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 } ## + ## # This generates roughly the same JavaScript as: + ## {.emit: "var obj = {a: 1, k: "foo", d: 42};".} + ## ``` let a = ident"a" var body = quote do: var `a` {.noinit.}: `typ` @@ -463,7 +468,7 @@ proc replaceSyms(n: NimNode): NimNode = for i in 0..<n.len: result[i] = replaceSyms(n[i]) -macro bindMethod*(procedure: typed): auto = +macro bindMethod*(procedure: typed): auto {.deprecated: "Don't use it with closures".} = ## Takes the name of a procedure and wraps it into a lambda missing the first ## argument, which passes the JavaScript builtin `this` as the first ## argument to the procedure. Returns the resulting lambda. @@ -471,24 +476,25 @@ macro bindMethod*(procedure: typed): auto = ## Example: ## ## We want to generate roughly this JavaScript: - ## - ## .. code-block:: js - ## var obj = {a: 10}; - ## obj.someMethod = function() { - ## return this.a + 42; - ## }; + ## ```js + ## var obj = {a: 10}; + ## obj.someMethod = function() { + ## return this.a + 42; + ## }; + ## ``` ## ## We can achieve this using the `bindMethod` macro: ## - ## .. code-block:: nim - ## let obj = JsObject{ a: 10 } - ## proc someMethodImpl(that: JsObject): int = - ## that.a.to(int) + 42 - ## obj.someMethod = bindMethod someMethodImpl + ## ```nim + ## let obj = JsObject{ a: 10 } + ## proc someMethodImpl(that: JsObject): int = + ## that.a.to(int) + 42 + ## obj.someMethod = bindMethod someMethodImpl ## - ## # Alternatively: - ## obj.someMethod = bindMethod - ## proc(that: JsObject): int = that.a.to(int) + 42 + ## # Alternatively: + ## obj.someMethod = bindMethod + ## proc(that: JsObject): int = that.a.to(int) + 42 + ## ``` if not (procedure.kind == nnkSym or procedure.kind == nnkLambda): error("Argument has to be a proc or a symbol corresponding to a proc.") var diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 19888aaa9..2d931eb20 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -34,6 +34,9 @@ func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.com func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".} ## Returns a new string with some or all matches of a pattern replaced by given replacement +func replace*(pattern: cstring, self: RegExp, cb: proc (args: varargs[cstring]): cstring): cstring {.importcpp.} + ## Returns a new string with some or all matches of a pattern replaced by given callback function + func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".} ## Divides a string into an ordered list of substrings and returns the array @@ -55,7 +58,7 @@ func contains*(pattern: cstring; self: RegExp): bool = assert jsregex in r"abc" assert jsregex notin r"abcd" assert "xabc".contains jsregex - asm "`result` = `self`.test(`pattern`);" + {.emit: "`result` = `self`.test(`pattern`);".} func startsWith*(pattern: cstring; self: RegExp): bool = ## Tests if string starts with given RegExp @@ -88,5 +91,7 @@ runnableExamples: assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] jsregex.compile(r"[lw]", r"i") assert "hello world".replace(jsregex,"X") == "heXlo world" + jsregex.compile(r"([a-z])\1*", r"g") + assert "abbcccdddd".replace(jsregex, proc (m: varargs[cstring]): cstring = ($m[0] & $(m.len)).cstring) == "a1b2c3d4" let digitsRegex: RegExp = newRegExp(r"\d") assert "foo".match(digitsRegex) == @[] |