Giter Club home page Giter Club logo

unrealscript-language-service's Introduction

Hi there ๐Ÿ‘‹

unrealscript-language-service's People

Contributors

davidoskky avatar dependabot-preview[bot] avatar dependabot[bot] avatar eliotvu avatar imgbotapp avatar shtoyan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

unrealscript-language-service's Issues

Support goto statements

goto noRecord; noRecord:

As it currently stands, goto statements are parsed and processed properly, but there is no intellIsense support yet, i.e. "noRecord" is never found.

[Feature Request]: Pre-Release versions for VSCode Marketplace

Is your feature request related to a problem? Please describe

Let's say I'm very happy with #148 fixed, and I can build from a fork for myself :d But lot's of people (at least some of my friends) will be lazy to set up a build system just to get the fixed-beta-minor version.

Describe the solution you'd like

VSCode has a pre-release feature, which allows to easily pick the latest, not tested version and check it. And it will allow more people to do it.

[0.4.4] Closing unopened comment

/**
 * Removes the values for all configurable properties in this object's class from the .ini file.
 *
 * @param	PropertyName		if specified, only this property's value will be removed.
 */
native static final function StaticClearConfig( optional string PropertyName );
*/

image

The UC compiler obviously considers this fine, so perhaps downgrade it to a suggestion or something?

QualifiedIdentifier's semmantics are inconsistent

Currently any qualified identifier is interpreted as Package.Class.Field or Class.Field.SubField, but depending on the scope the UnrealScript will interpret these differently.

E.g. a qualified identifier as Package.Class is in most cases only valid when extending a class, or implementing a class.

Valid:
class MyObject extends Core.Object;

Invalid:
var Core.Object AnObject;

It appears that when declaring a variable, a qualified identifier will always be interpreted as Class.EnumOrStruct, deeply referencing nested enums or structs are also invalid.

var class<Core.Object> MyMetaClass; is also invalid and thus inconsistent with the extends semantic.

As it currently stands the LSP interprets everything as PackageOrAny?.AnyField?.AnySubField?....

Travis Failure

ERROR in ./src/UC/Parser/CommonTokenStreamExt.ts

Module not found: Error: Can't resolve '../../antlr/UCLexer' in '/home/travis/build/EliotVU/UnrealScript-Language-Service/server/src/UC/Parser'

 @ ./src/UC/Parser/CommonTokenStreamExt.ts 6:18-48

 @ ./src/UC/document.ts

 @ ./src/UC/indexer.ts

 @ ./src/server.ts

Travis appears to skip the ANTLR step when building the extension for production.

[0.4.4] Arrays

Generic arguments are not recognized but non-generic (eg. Remove) are fine
image
image

There is also a RandomizeOrder function which isn't recognized. I suspect that it's a Firaxis addition since I can find uses only inside XComGame
image

[0.4.3] "const ref" argument confusing the parser

image

static native function bool IsUnitInRange(const ref XComGameState_Unit SourceUnit, const ref XComGameState_Unit TargetUnit,
										  float MinRange = 0.0f, float MaxRange = 0.0f, float MaxAngle = 360.0f);

static native function bool IsUnitInRangeFromLocations(const ref XComGameState_Unit SourceUnit, const ref XComGameState_Unit TargetUnit,
										  const out TTile SourceTile, const out TTile TargetTile, 
										  float MinRange = 0.0f, float MaxRange = 0.0f, float MaxAngle = 360.0f);
Encountered an error while constructing the body for function 'XComGame.Helpers.IsUnitInRange' Error: The specified node does not exist
    at Tt.getChild (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:1695)
    at Tt.getRuleContext (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:2402)
    at Tt.functionBody (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:277874)
    at e.DocumentASTWalker.visitFunctionDecl (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:596:22518)
    at Tt.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:278381)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at O.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:252506)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at R.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:251720)
    at e.DocumentASTWalker.visit (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:513)
Encountered an error while constructing the body for function 'XComGame.Helpers.XComGameState_Unit' Error: The specified node does not exist
    at Tt.getChild (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:1695)
    at Tt.getRuleContext (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:2402)
    at Tt.functionBody (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:277874)
    at e.DocumentASTWalker.visitFunctionDecl (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:596:22518)
    at Tt.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:278381)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at O.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:252506)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at R.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:251720)
    at e.DocumentASTWalker.visit (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:513)
Encountered an error while constructing the body for function 'XComGame.Helpers.IsUnitInRangeFromLocations' Error: The specified node does not exist
    at Tt.getChild (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:1695)
    at Tt.getRuleContext (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:2402)
    at Tt.functionBody (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:277874)
    at e.DocumentASTWalker.visitFunctionDecl (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:596:22518)
    at Tt.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:278381)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at O.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:252506)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at R.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:251720)
    at e.DocumentASTWalker.visit (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:513)
Encountered an error while constructing the body for function 'XComGame.Helpers.XComGameState_Unit' Error: The specified node does not exist
    at Tt.getChild (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:1695)
    at Tt.getRuleContext (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:2402)
    at Tt.functionBody (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:277874)
    at e.DocumentASTWalker.visitFunctionDecl (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:596:22518)
    at Tt.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:278381)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at O.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:252506)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at R.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:251720)
    at e.DocumentASTWalker.visit (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:513)
Encountered an error while constructing the body for function 'XComGame.Helpers.TTile' Error: The specified node does not exist
    at Tt.getChild (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:1695)
    at Tt.getRuleContext (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:40:2402)
    at Tt.functionBody (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:277874)
    at e.DocumentASTWalker.visitFunctionDecl (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:596:22518)
    at Tt.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:278381)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at O.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:252506)
    at e.DocumentASTWalker.visitChildren (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:658)
    at R.accept (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:256:251720)
    at e.DocumentASTWalker.visit (c:\Users\xyman\.vscode\extensions\eliotvu.uc-0.4.3\server\out\server.js:600:513)
Helpers: Walking time 17.128762990236282
Helpers: linking time 2.1040569841861725
[Helpers]: post linking time 6.587505012750626

Make error squiggles optional

Right now everything works wonders except for the error squiggles. Since the Intellisense doesn't know how to deal with native functions/classes that don't have declarations in the immediate workspace, nearly every script has errors all over the place. Can we get an option to turn this off?

VSCode's Sticky scroll feature

Describe the solution you'd like

VSCode introduced sticky scroll feature in V1.71.

GIF

It's a killer feature when you browse huge functions and especially with several classes opened at the same time.

Aaand it doesn't work your extension which is a unfortunate.

[Feature] SemanticTokens

This issue serves as a form to track the progress towards implementing the SemanticTokens feature of the LSP API.

image

Support special const values.

UnrealScript has some quirky special const tokens such as "sizeof", "nameof", and "arraycount". Where "sizeof" in particular can only be used within a const declaration.

const CONST_SIZEOF = sizeof(Object);

const CONST_ARRAYCOUNT = arraycount(staticArray);

const CONST_CLASS = class'Object';

const CONST_VECT = vect(0,0,0);

const CONST_ROT = rot(0,0,0);

const CONST_RNG = rng(0,0);

const CONST_NAMEOF = nameof(staticArray);

[Bug]: Multiple mod directories mess up class's field hint

Describe the bug

If you have multiple utility classes across different packages with the same name:

Workspace
|-- US Sources of the game
|   `-- ...
|-- MYMod_1
|   |-- ...
|   `-- Settings.uc
`-- MYMod_2
    |-- ...
    `-- Settings.uc

and try to access default variables:

class'Settings'.default.bHidePortraits

extension will show class hints only for one of the packages. For others it will warn variable does not exist on type MYMod_2.Settings.

Screenshots

image

Extension doesn't index files added via copy-paste

Describe the bug

After copying files (outside of VSCode) into the workspace's directory they do not get indexed - no definitions are exported from them until VSCode restarts. You can't view the parents, super code or anything else.

image

Enabling-disabling this option, or editing-saving new files to trigger the reindexing doesn't fix the issue.

[0.5.0] (DefaultProperties) Issue with structs written on multiple lines

After writing a struct on multiple lines in DefaultProperties, everything else below it loses colors. It only happens on 0.5.0.
image

It doesn't happen when writing it on a single line without the curly brackets.
image

Seems like the closing bracket causes it, since removing it (aside from causing a syntax error) restores the colors:
image

Add configurable SDK location

References from the Killing Floor 2 SDK cannot be found since they reside somewhere else than the mod folder.

Source files for the the mod packages are located in:
<drive:>\Users\<user>\Documents\My Games\KillingFloor2\KFGame\Src\MyPackage

However, SDK source files are located in:
<drive:>\Steam\SteamApps\common\killingfloor2\development\Src

Hence the missing references.

Source

[Bug]: No symbols found

Describe the bug

After 0.6.0 symbols started to appear in Outline tab. But they behave quite strangely:

  • For some classes they just work fine and instantly.
  • For others it will warn you no symbols found in document and show nothing. It doesn't depend on a file size - huge hud files can work and small utility classes will bug out.

You can fix it by editing-saving the file or switching to other classes several times.

Screenshots

Not a screenshot but a short video demonstration: https://youtu.be/h22MRGMKCwI

Object literal references to assets are being interpreted as types

Describe the bug
Object literals that reference asset files emit errors that the internal reference string is a missing type.

Expected behavior
The type lookups should only be done for class and enum object literals. The extension should make no attempt to do sanity checks on the internal reference string for any other object literal.

Screenshots
image

Error highlighting is completely broken, please let me turn it off

Please give me the ability to turn off error highlighting. I'm using this extension on unrealscript 1 files and it completely breaks down. All comparison operators are "invalid". Most functions from external packages are considered to not exist.

This extension has most of the necessary highlighting to be fully functional (it still lacks proper highlighting for Class and Name type), however the error highlighting has never worked since the beginning and it can just go with no value loss.

Context:
Extension settings: All 0.4.3 default settings with Unrealscript generation set to 1 (changing the generation DOES NOT fix this).
Workspace: All folders for the package sources are included.
error

Please, this is a great extension and it helps a lot. It does not need error highlighting, and error highlighting is the only thing that's wrong with this extension, please turn it off.

Delegate is an unknown type!

The current indexer will fail to link the type "delegate", as seen in this line of code:
var array<delegate<CanUnpause> > Pausers;

Can not got to external defenition

Hello, thank you for making this extension!
I am tiring to use this extension to write some modes for xcom 2
But can not figure out how to add external definitions for game specific code to the intellisense
Is there are some config option for this?

Parsing metadata: "Extraneous input '|'"

The UnrealScript grammar currently does not properly support meta data bodies, that have an extra meta tag.

The following leads to a parsing error, stating: "Extraneous input '|'":
var(Display) const interp float DrawScale <UIMin=0.1 | UIMax=4.0>;

[0.5.0 - 0.6.0]: Auto-completion hangs sometimes

Describe the bug

The Language-Server will sometimes hang and stop responding all together, upon further re-search this occurs if the auto-completion parser hits a block like cpptext { code... }, this appears to cause an infinite loop in the third-party library antlr4-c3.

I have submitted an issue for this at antlr4-c3/issues/74.

[0.4.4] Maths operations are considered to result in a struct

static final simulated function float FPctByRange( float Value, float InMin, float InMax )
{
	return (Value - InMin) / (InMax - InMin);
}

image

static final simulated function int ClampRotAxisFromBase(int Current, int Center, int MaxDelta)
{
	local int DeltaFromCenter;

	DeltaFromCenter = NormalizeRotAxis(Current - Center);

image

Angle = Acos( FClamp( Dir.X, -1.f, 1.f ) );

image
image

Const fields can be declared within any struct field

E.g.

const A = 0;
function Test() {
       Log(A);
       Log(B);

       const C = 0;
}

function Test2() {
    const B = 0;
    Log(C);
}

Is valid code but currently not supported if a const is declared after an expression, nor is Log(C) valid as C cannot be found.

[Bug]: Assigning an object to a string (DefaultProperties)

Describe the bug

The server is missing support for assigning an object using a string literal, and reports the error Type 'String' is not assignable to type 'Object if type checking is enabled.

defaultproperties
{
     // Not supported
     DefaultTexture="EngineResources.WhiteSquareTexture"
     // Supported
     DefaultTexture=Texture2D'EngineResources.WhiteSquareTexture'
}

Screenshots

No response

0.4.0 endless parsing

I've tried 0.4.0 and I've been met with endless parsing.

Mod: https://github.com/WOTCStrategyOverhaul/X2WOTCCommunityHighlander/tree/development
I've also added SrcOrig as per your recommendation as "Add folder to workspace"

Output: https://gist.github.com/Xymanek/cb912eb539f3d13c49fd9a3ae7c7b05d Note the following:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 00007FF7EBA076CF 
 2: 00007FF7EA69CA46 
 3: 00007FF7EA69CC56 
 4: 00007FF7E9883828 
 5: 00007FF7E98837C9 
 6: 00007FF7E98FC110 
 7: 00007FF7E98FAF14 
 8: 00007FF7E98F8AC9 
 9: 00007FF7E99043FB 
10: 00007FF7E99044A9 
11: 00007FF7E951DC4D 
12: 00007FF7E96CFC69 
13: 00007FF7E9AEFDF2 
[Info  - 11:00:03 AM] Connection to server got closed. Server will restart.

This happens in an endless loop. At the point when this happens at least 4-5 GB of RAM is free

[Suggestion] Chat platform

Github issues are great for tracking and coordinating on long-term tasks but isn't really suitable for generic discussion. I suggest making a room on gitter.im so that we could discuss/coordinate(1) things easier

(1) For example, the question I would like to ask right now is "How far away is the next release? Should I make issues now or wait till next release (since I saw that there were a few commits already)"

TypeError: e.findSuperSymbol is not a function

I'm having this error repeatedly until server stops restarting. The codebase I'm working on is pretty huge

c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:68
*/Object.defineProperty(e,"__esModule",{value:!0});e.RecognitionException=class extends Error{constructor(t,e,r,n){super(n),this._offendingState=-1,this._recognizer=t,this.input=e,this.ctx=r,t&&(this._offendingState=t.state)}get offendingState(){return this._offendingState}setOffendingState(t){this._offendingState=t}get expectedTokens(){if(this._recognizer)return this._recognizer.atn.getExpectedTokens(this._offendingState,this.ctx)}get context(){return this.ctx}get inputStream(){return this.input}getOffendingToken(t){if(!t||t===this._recognizer)return this.offendingToken}setOffendingToken(t,e){t===this._recognizer&&(this.offendingToken=e)}get recognizer(){return this._recognizer}}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(278),i={};class s{constructor(t,e){this.hash=t,this.text=e,i[t]=this}toString(){return this.text}}function o(t){const e=n.str(t.toLowerCase());return i[e]||new s(e,t)}

TypeError: e.findSuperSymbol is not a function
at l.index (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:364:5417)
at e.UCQualifiedTypeSymbol.index (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:364:2600)
at e.UCObjectLiteral.index (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:364:21347)
at e.UCDefaultAssignmentExpression.index (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:364:16405)
at e.UCBlock.index (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:2504)
at e.DocumentIndexer.visitDefaultPropertiesBlock (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:4913)
at e.UCDefaultPropertiesBlock.accept (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:20680)
at e.DocumentIndexer.visitStructBase (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:4642)
at e.DocumentIndexer.visitClass (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:4690)
at e.UCDocumentClassSymbol.accept (c:\Users\mssol.vscode\extensions\eliotvu.uc-0.4.1\server\out\server.js:600:19023)
[Info - 7:32:11 PM] Connection to server got closed. Server will restart.

Preprocessor macros

The usage of macros can lead to severe parsing and indexing issues, like the following code:

return ( `TimeSince( LastRenderTime ) < 0.3 );

The usage of `TimeSince here will tell the lexer that this is a PP command, when this is occurred the input will be thrown out thus leaving the parser with:

return ( < 0.3 );

Which leads to even bigger issues down the road, so as the end block of a method not being terminated.

One workaround for this issue could be to safely parse binary operators that are missing an expression on the left or right, however due the nature of UnrealScript allowance of custom defined pre and post operators, "< 0.3" could in theory be a post operator! Thus it cannot be assumed to be an incomplete binary operator (albeit it would be totally unexpected for "<" symbol, that cannot be said of other symbols).

The proper solution would be not to skip any PP commands and instead fully parse and evaluate any macro prior to parsing the document, this is however not an easy task!

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.