GithubHelp home page GithubHelp logo

carbonblack / intellij-rpmspec Goto Github PK

View Code? Open in Web Editor NEW
11.0 4.0 4.0 775 KB

An IntelliJ plugin for RPM SPEC file support

License: Apache License 2.0

Kotlin 87.33% Lex 12.67%
intellij rpm specfile intellij-plugin

intellij-rpmspec's Introduction

plugin icon

RPM SPEC file support IntelliJ

Build Version Downloads GitHub license

IntelliJ plugin for basic support of RPM SPEC files.

screenshot

Adds syntax highlighting, and basic support for macros.

Installation

Install the plugin directly from your IDE or the Jetbrains plugin repository.

Feature summary

  • Syntax highlighting of SPEC files
  • References, go-to definition, and find usage of macros
  • Discovery of macros on the OS macro path
  • Detection of unresolved macros
  • Basic structure view
  • Code folding of if statements
  • Brace matching
  • Line commenting

Contributing

Small and large contributions welcome! For new features or substantial changes, please open an issue beforehand so that it can be discussed.

intellij-rpmspec's People

Contributors

tlusk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

intellij-rpmspec's Issues

IDE Error

I got an error on the latest version of this plugin on version 0.9.7
Goland 2019.1.3 Build 191.7479.32

java.nio.file.NoSuchFileException: /usr/local/Cellar/rpm/4.14.2.1/lib/rpm/macros.d
	at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
	at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
	at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
	at sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55)
	at sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:144)
	at java.nio.file.Files.readAttributes(Files.java:1737)
	at java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219)
	at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276)
	at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:322)
	at java.nio.file.FileTreeIterator.<init>(FileTreeIterator.java:72)
	at java.nio.file.Files.walk(Files.java:3574)
	at java.nio.file.Files.walk(Files.java:3625)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil$macroPathFiles$2.invoke(RpmMacroUtil.kt:66)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil$macroPathFiles$2.invoke(RpmMacroUtil.kt:24)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil.getMacroPathFiles(RpmMacroUtil.kt)
	at com.carbonblack.intellij.rpmmacro.RpmMacroFileType.isMyFileType(RpmMacroFileType.kt:16)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.getByFile(FileTypeManagerImpl.java:568)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.getFileTypeByFile(FileTypeManagerImpl.java:540)
	at com.intellij.openapi.vfs.VirtualFile.getFileType(VirtualFile.java:327)
	at com.intellij.psi.impl.file.impl.FileManagerImpl.createFileViewProvider(FileManagerImpl.java:258)
	at com.intellij.psi.impl.file.impl.FileManagerImpl.findViewProvider(FileManagerImpl.java:205)
	at com.intellij.psi.impl.file.impl.FileManagerImpl.findFile(FileManagerImpl.java:365)
	at com.intellij.psi.impl.PsiManagerImpl.findFile(PsiManagerImpl.java:160)
	at com.intellij.psi.impl.file.PsiDirectoryImpl.getFiles(PsiDirectoryImpl.java:146)
	at com.goide.sdk.GoPackageUtil.getAllPackagesInDirectoryInner(GoPackageUtil.java:106)
	at com.goide.sdk.GoPackageUtil.getAllPackagesInDirectory(GoPackageUtil.java:88)
	at com.goide.psi.impl.GoPackage.in(GoPackage.java:108)
	at com.goide.psi.impl.imports.DefaultGoImportResolver.lambda$findPackagesInContexts$4(DefaultGoImportResolver.java:122)
	at one.util.streamex.AbstractStreamEx.lambda$flatCollection$16(AbstractStreamEx.java:556)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:267)
	at java.util.stream.SliceOps$1$1.accept(SliceOps.java:204)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359)
	at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
	at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:545)
	at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
	at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:438)
	at one.util.streamex.AbstractStreamEx.toArray(AbstractStreamEx.java:344)
	at one.util.streamex.AbstractStreamEx.toList(AbstractStreamEx.java:1154)
	at com.goide.psi.impl.imports.DefaultGoImportResolver.findPackagesInContexts(DefaultGoImportResolver.java:123)
	at com.goide.psi.impl.imports.DefaultGoImportResolver.innerResolve(DefaultGoImportResolver.java:67)
	at com.goide.psi.impl.imports.DefaultGoImportResolver.lambda$null$0(DefaultGoImportResolver.java:50)
	at com.intellij.util.containers.ConcurrentFactoryMap$3.create(ConcurrentFactoryMap.java:192)
	at com.intellij.util.containers.ConcurrentFactoryMap.get(ConcurrentFactoryMap.java:45)
	at com.goide.psi.impl.imports.DefaultGoImportResolver.resolve(DefaultGoImportResolver.java:55)
	at com.goide.sdk.GoPackageUtil.findByImportPath(GoPackageUtil.java:168)
	at com.goide.execution.testing.GoTestRunConfiguration.checkConfiguration(GoTestRunConfiguration.java:133)
	at com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl.checkSettings(RunnerAndConfigurationSettingsImpl.kt:310)
	at com.intellij.execution.RunnerAndConfigurationSettings.checkSettings(RunnerAndConfigurationSettings.java:116)
	at com.intellij.execution.impl.TimedIconCache.calcIcon(TimedIconCache.kt:71)
	at com.intellij.execution.impl.TimedIconCache.access$calcIcon(TimedIconCache.kt:18)
	at com.intellij.execution.impl.TimedIconCache$get$$inlined$write$lambda$1.fun(TimedIconCache.kt:50)
	at com.intellij.execution.impl.TimedIconCache$get$$inlined$write$lambda$1.fun(TimedIconCache.kt:18)
	at com.intellij.ui.DeferredIconImpl.evaluate(DeferredIconImpl.java:280)
	at com.intellij.ui.DeferredIconImpl.lambda$null$0(DeferredIconImpl.java:165)
	at com.intellij.ui.IconDeferrerImpl.evaluateDeferred(IconDeferrerImpl.java:111)
	at com.intellij.ui.DeferredIconImpl.lambda$null$1(DeferredIconImpl.java:165)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1166)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runInReadActionWithWriteActionPriority$0(ProgressIndicatorUtils.java:68)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runWithWriteActionPriority$1(ProgressIndicatorUtils.java:121)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:164)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:586)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:532)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:86)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:151)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runWithWriteActionPriority(ProgressIndicatorUtils.java:110)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:68)
	at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:89)
	at com.intellij.ui.DeferredIconImpl.lambda$paintIcon$4(DeferredIconImpl.java:164)
	at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:220)
	at com.intellij.util.concurrency.BoundedTaskExecutor.access$100(BoundedTaskExecutor.java:26)
	at com.intellij.util.concurrency.BoundedTaskExecutor$2.lambda$run$0(BoundedTaskExecutor.java:198)
	at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:224)
	at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:194)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Conditional macro expansion is not correctly parsed

I have a spec file with conditionals for handling distribution-specific requirements. One difference between distributions is that some of them require specifying a minimum version for some packages.

When building for those distributions, macros are defined named packagename_ver. Then, the dependencies for those packages are as follows:

Requires: packagename %{?packagename_ver:>= %{packagename_ver}}

I.E. If the packagename_ver macro is defined as 1.2.3, then it should expand to...

Requires: packagename >= 1.2.3

...and if it isn't defined, it will expand to just...

Requires: packagename

The issue I'm having is that the RPM Spec File plugin doesn't support these conditional expansions. For those lines, it emits the following error during code analysis:

Error:(122, 45) RpmSpecTokenType.!, RpmSpecTokenType.%, RpmSpecTokenType.(, RpmSpecTokenType.), RpmSpecTokenType.<, RpmSpecTokenType.>, RpmSpecTokenType.?, RpmSpecTokenType.BOOL_LITERAL, RpmSpecTokenType.CODE, RpmSpecTokenType.COMMENT, RpmSpecTokenType.FILES_DIRECTIVE_TOKEN, RpmSpecTokenType.FLOAT_LITERAL, RpmSpecTokenType.IDENTIFIER, RpmSpecTokenType.INTEGER_LITERAL, RpmSpecTokenType.\ or RpmSpecTokenType.if expected, got '='

Support for autospec

rpmautospec has two macros that are not accounted for in this plugin:%autochangelog and %autorelease

Example spec:

# Allowed function arguments can be referenced at https://docs.pagure.org/fedora-infra.rpmautospec/autorelease.html
Version:    %autorelease

%changelog
%autochangelog

If the changelog could be viewed when hovering over %autochangelog that would be magical, but for now, simply not outputting an error would be sufficient.

langauge injection

Hi,

I would like to know if this plugins supports language injection

since inside %build or %install shell script are used, I would like to use the Shell Script assistance for shell scripts for IntelliJ

Icon not rendered correctly

Both in Goland 2019.3.1, and IntelliJ IDEA 2020.1 (Community Edition), the icon does not render correctly; the background of the icon is scaled, but filled "red" instead of transparent. (Possibly the Intellij platform doesn't handle the gradients correctly?)

I'm running on macOS Mojave (10.4.6) on a retina screen. Here's some screenshots:

Screenshot 2020-04-10 at 09 20 37

Screenshot 2020-04-10 at 09 20 43

Screenshot 2020-04-10 at 09 31 31

The problem reproduced both in "dark" (darkula) and light mode

And info of the versions I'm running;

Screenshot 2020-04-10 at 09 22 26

Screenshot 2020-04-10 at 09 35 07

I tried looking at the docs but nothing really stood out (other than there's no _dark variants of the icon); https://www.jetbrains.org/intellij/sdk/docs/reference_guide/work_with_icons_and_images.html

The naming notation used for PNG icons (see below) is still relevant. However, the @2x version of an SVG icon should still provide the same base size. The icon graphics of such an icon can be expressed in more details via double precision. If the icon graphics are simple enough so that it renders perfectly in every scale, then the @2x version can be omitted.

`%files` directive not recognised after `%install` directive

Describe the bug

I noticed some strange behaviour in the syntax highlighting of a .spec file, when an %install directive is present. For example, take the following spec file and open it in IntelliJ:

Name: Some name
Version: 0.0.1

%install
mktemp -d

%files
%defattr(-,-,-,-)
/opt/some/file

IntelliJ shows it like this:
image

Note the non-highlighted %files directive. If I now put my cursor at the end of the line containing %files and I press <enter>, the line becomes highlighted in yellow. If I then press <del> to remove the newly added line, the %files directive remains yellow. Until I close the file and open it again, then it's not recognised and white again.

It seems to happen after all directives from the SHELL_SECTIONS constant. For example: if I change %install into %description, the problem is gone. If I change it to %build, the issue is there as well.

Reproduction steps

  1. Create a simple .spec file with an %install directive, followed by a %files directive.
  2. Notice that the %files directive is not highlighted.

Expected behavior

I expect the %files directive to be highlighted in yellow, regardless of the presence of an %install directive before it.

Additional context

Using IntelliJ 2022.3 with plugin version 2.1.0. I reproduced it with ./gradlew runIde.

Support for buildtools

Technically outside the scope of this plugin, but would be nice to add the common buildtools for spec building:

  • rpmbuild
  • mock
  • copr
  • fedpkg

Cannot run program "rpm" on ArchLinux

At the startup PyCharm throws an exception

java.io.IOException: Cannot run program "rpm": error=2, No such file or directory
	at java.base/java.lang.ProcessBuilder.start(Unknown Source)
	at java.base/java.lang.ProcessBuilder.start(Unknown Source)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil.runCommand(RpmMacroUtil.kt:33)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil.runCommand$default(RpmMacroUtil.kt:26)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil$macroPathFiles$2.invoke(RpmMacroUtil.kt:45)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil$macroPathFiles$2.invoke(RpmMacroUtil.kt:24)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at com.carbonblack.intellij.rpmmacro.RpmMacroUtil.getMacroPathFiles(RpmMacroUtil.kt)
	at com.carbonblack.intellij.rpmmacro.RpmMacroFileType.isMyFileType(RpmMacroFileType.kt:16)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.getByFile(FileTypeManagerImpl.java:568)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.getFileTypeByFile(FileTypeManagerImpl.java:540)
	at com.intellij.openapi.vfs.VirtualFile.getFileType(VirtualFile.java:327)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.isBinaryWithoutDecompiler(FileDocumentManagerImpl.java:579)
	at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.getDocument(FileDocumentManagerImpl.java:176)
	at com.intellij.openapi.fileEditor.impl.EditorsSplitters$UIBuilder.lambda$processFiles$1(EditorsSplitters.java:851)
	at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:974)
	at com.intellij.openapi.application.ReadAction.compute(ReadAction.java:57)
	at com.intellij.openapi.fileEditor.impl.EditorsSplitters$UIBuilder.processFiles(EditorsSplitters.java:851)
	at com.intellij.openapi.fileEditor.impl.EditorsSplitters$UIBuilder.processFiles(EditorsSplitters.java:819)
	at com.intellij.openapi.fileEditor.impl.EditorsSplitters$ConfigTreeReader.process(EditorsSplitters.java:810)
	at com.intellij.openapi.fileEditor.impl.EditorsSplitters.openFiles(EditorsSplitters.java:239)
	at com.intellij.openapi.fileEditor.impl.OpenFilesActivity.runActivity(OpenFilesActivity.java:34)
	at com.intellij.ide.startup.impl.StartupManagerImpl.lambda$logActivityDuration$2(StartupManagerImpl.java:152)
	at com.intellij.util.TimeoutUtil.measureExecutionTime(TimeoutUtil.java:74)
	at com.intellij.ide.startup.impl.StartupManagerImpl.logActivityDuration(StartupManagerImpl.java:152)
	at com.intellij.ide.startup.impl.StartupManagerImpl.lambda$runPostStartupActivitiesFromExtensions$1(StartupManagerImpl.java:140)
	at com.intellij.ide.startup.impl.StartupManagerImpl.runActivity(StartupManagerImpl.java:360)
	at com.intellij.ide.startup.impl.StartupManagerImpl.runPostStartupActivitiesFromExtensions(StartupManagerImpl.java:142)
	at com.intellij.openapi.project.impl.ProjectManagerImpl.lambda$openProject$8(ProjectManagerImpl.java:407)
	at com.intellij.openapi.progress.impl.CoreProgressManager$2.run(CoreProgressManager.java:248)
	at com.intellij.openapi.progress.impl.CoreProgressManager$TaskRunnable.run(CoreProgressManager.java:731)
	at com.intellij.openapi.progress.impl.CoreProgressManager$5.run(CoreProgressManager.java:442)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:164)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:586)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:532)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:86)
	at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:151)
	at com.intellij.openapi.application.impl.ApplicationImpl.lambda$null$10(ApplicationImpl.java:594)
	at com.intellij.openapi.application.impl.ApplicationImpl$1.run(ApplicationImpl.java:311)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException: error=2, No such file or directory
	at java.base/java.lang.ProcessImpl.forkAndExec(Native Method)
	at java.base/java.lang.ProcessImpl.<init>(Unknown Source)
	at java.base/java.lang.ProcessImpl.start(Unknown Source)
	... 44 more

Add support for jinja templates

Is your feature request related to a problem? Please describe.

Can't combine jinja template with rpm spec plugins

Describe the solution you'd like

Jinja 2 template plugin in PyCharm can be used with "Template data language"
I wan't to combine jinja and rpm specs, but when a use for example
%global version {{ pkg_version }}
I receive error from plugin

RpmSpecTokenType.%, RpmSpecTokenType.%%, RpmSpecTokenType.(, RpmSpecTokenType.), RpmSpecTokenType.-, RpmSpecTokenType.:, RpmSpecTokenType.<, RpmSpecTokenType.>, RpmSpecTokenType.BOOL_LITERAL, RpmSpecTokenType.CODE, RpmSpecTokenType.FLOAT_LITERAL, RpmSpecTokenType.IDENTIFIER, RpmSpecTokenType.INTEGER_LITERAL, RpmSpecTokenType.\ or RpmSpecTokenType.{ expected

Can you add support for jinja like #5
Or maybe exclude {{ }} from errors

Describe alternatives you've considered

No response

Additional context

No response

A wrong prompt

Hi,
When I define a macro,about this:
%define _rpmfilename %%{NAME}-%%{VERSION}-1.0-%%{RELEASE}.noarch.rpm

I got a wrong prompt at "%%{":
"should be , , RpmSpecTokenType.%, RpmSpecTokenType.%%, RpmSpecTokenType.(, RpmSpecTokenType.), RpmSpecTokenType.-, RpmSpecTokenType.:, RpmSpecTokenType.<, RpmSpecTokenType.>, RpmSpecTokenType.BOOL_LITERAL, RpmSpecTokenType.CODE, RpmSpecTokenType.COMMENT, RpmSpecTokenType.EOL, RpmSpecTokenType.FLOAT_LITERAL, RpmSpecTokenType.IDENTIFIER, RpmSpecTokenType.INTEGER_LITERAL, RpmSpecTokenType., RpmSpecTokenType.changelog, RpmSpecTokenType.description, RpmSpecTokenType.files, RpmSpecTokenType.if, RpmSpecTokenType.package or RpmSpecTokenType.undefine, get '{'"

Fix typo

Describe the bug

You might want to fix the typo in ktlint_experimental to enable the experimental rules of ktlint:

    kotlin {
        ktlint(ktlintVersion)
            .editorConfigOverride(mapOf("ktlint_experimentasl" to "enabled"))
    }

Also, you can remove property below as it is invalid (the correct property is already on the line below it):

ktlint_experimental_property-naming = disabled

Reproduction steps

N/A

Expected behavior

N/A

Additional context

No response

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.