Paweł Bylica
10 years ago
51 changed files with 10320 additions and 910 deletions
@ -1,122 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file AppContext.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly. |
|||
* In the future this class can be extended to add more variable related to the context of the application. |
|||
* For now AppContext provides reference to: |
|||
* - QQmlApplicationEngine |
|||
* - dev::WebThreeDirect (and dev::eth::Client) |
|||
* - KeyEventManager |
|||
*/ |
|||
|
|||
#include <QMessageBox> |
|||
#include <QClipboard> |
|||
#include <QQmlComponent> |
|||
#include <QQmlContext> |
|||
#include <QQmlApplicationEngine> |
|||
#include <QWindow> |
|||
#include "CodeModel.h" |
|||
#include "FileIo.h" |
|||
#include "ClientModel.h" |
|||
#include "CodeEditorExtensionManager.h" |
|||
#include "Exceptions.h" |
|||
#include "QEther.h" |
|||
#include "QVariableDefinition.h" |
|||
#include "HttpServer.h" |
|||
#include "AppContext.h" |
|||
#include "SortFilterProxyModel.h" |
|||
|
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using namespace dev::mix; |
|||
|
|||
const QString c_projectFileName = "project.mix"; |
|||
|
|||
AppContext::AppContext(QQmlApplicationEngine* _engine) |
|||
{ |
|||
m_applicationEngine = _engine; |
|||
m_codeModel.reset(new CodeModel(this)); |
|||
m_clientModel.reset(new ClientModel(this)); |
|||
m_fileIo.reset(new FileIo()); |
|||
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();}); |
|||
} |
|||
|
|||
AppContext::~AppContext() |
|||
{ |
|||
} |
|||
|
|||
void AppContext::load() |
|||
{ |
|||
m_applicationEngine->rootContext()->setContextProperty("appContext", this); |
|||
QFont f; |
|||
m_applicationEngine->rootContext()->setContextProperty("systemPointSize", f.pointSize()); |
|||
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo"); |
|||
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get()); |
|||
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get()); |
|||
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther"); |
|||
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt"); |
|||
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); |
|||
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); |
|||
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel"); |
|||
qmlRegisterType<QSolidityType>("org.ethereum.qml.QSolidityType", 1, 0, "QSolidityType"); |
|||
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml")); |
|||
QObject* projectModel = projectModelComponent.create(); |
|||
if (projectModelComponent.isError()) |
|||
{ |
|||
QmlLoadException exception; |
|||
for (auto const& e : projectModelComponent.errors()) |
|||
exception << QmlErrorInfo(e); |
|||
BOOST_THROW_EXCEPTION(exception); |
|||
} |
|||
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); |
|||
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); |
|||
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer"); |
|||
|
|||
m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); |
|||
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0)); |
|||
window->setIcon(QIcon(":/res/mix_256x256x32.png")); |
|||
appLoaded(); |
|||
} |
|||
|
|||
QQmlApplicationEngine* AppContext::appEngine() |
|||
{ |
|||
return m_applicationEngine; |
|||
} |
|||
|
|||
void AppContext::displayMessageDialog(QString _title, QString _message) |
|||
{ |
|||
// TODO : move to a UI dedicated layer.
|
|||
QObject* dialogWin = m_applicationEngine->rootObjects().at(0)->findChild<QObject*>("alertMessageDialog", Qt::FindChildrenRecursively); |
|||
QObject* dialogWinComponent = m_applicationEngine->rootObjects().at(0)->findChild<QObject*>("alertMessageDialogContent", Qt::FindChildrenRecursively); |
|||
dialogWinComponent->setProperty("source", QString("qrc:/qml/BasicMessage.qml")); |
|||
dialogWin->setProperty("title", _title); |
|||
dialogWin->setProperty("width", "250"); |
|||
dialogWin->setProperty("height", "100"); |
|||
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message); |
|||
QMetaObject::invokeMethod(dialogWin, "open"); |
|||
} |
|||
|
|||
QString AppContext::clipboard() const |
|||
{ |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
return clipboard->text(); |
|||
} |
|||
|
|||
void AppContext::toClipboard(QString _text) |
|||
{ |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
clipboard->setText(_text); |
|||
} |
@ -1,88 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file AppContext.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly. |
|||
* In the future this class can be extended to add more variable related to the context of the application. |
|||
* For now AppContext provides reference to: |
|||
* - QQmlApplicationEngine |
|||
* - dev::WebThreeDirect (and dev::eth::Client) |
|||
* - KeyEventManager |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <QUrl> |
|||
#include <QObject> |
|||
|
|||
class QQmlApplicationEngine; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class CodeModel; |
|||
class ClientModel; |
|||
class FileIo; |
|||
/**
|
|||
* @brief Provides access to application scope variable. |
|||
*/ |
|||
|
|||
class AppContext: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
Q_PROPERTY(QString clipboard READ clipboard WRITE toClipboard NOTIFY clipboardChanged) |
|||
|
|||
public: |
|||
AppContext(QQmlApplicationEngine* _engine); |
|||
virtual ~AppContext(); |
|||
/// Load the UI from qml files
|
|||
void load(); |
|||
/// Get the current QQMLApplicationEngine instance.
|
|||
QQmlApplicationEngine* appEngine(); |
|||
/// Get code model
|
|||
CodeModel* codeModel() { return m_codeModel.get(); } |
|||
/// Get client model
|
|||
ClientModel* clientModel() { return m_clientModel.get(); } |
|||
/// Display an alert message.
|
|||
void displayMessageDialog(QString _title, QString _message); |
|||
/// Copy text to clipboard
|
|||
Q_INVOKABLE void toClipboard(QString _text); |
|||
/// Get text from clipboard
|
|||
QString clipboard() const; |
|||
|
|||
signals: |
|||
/// Triggered once components have been loaded
|
|||
void appLoaded(); |
|||
void clipboardChanged(); |
|||
|
|||
private: |
|||
QQmlApplicationEngine* m_applicationEngine; //owned by app
|
|||
std::unique_ptr<CodeModel> m_codeModel; |
|||
std::unique_ptr<ClientModel> m_clientModel; |
|||
std::unique_ptr<FileIo> m_fileIo; |
|||
|
|||
public slots: |
|||
/// Delete the current instance when application quit.
|
|||
void quitApplication() {} |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,55 +1,40 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ConstantCompilationControl.cpp
|
|||
/** @file Clipboard.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include <QQmlContext> |
|||
#include <QQuickItem> |
|||
#include <QtCore/QFileInfo> |
|||
#include "Clipboard.h" |
|||
#include <QApplication> |
|||
#include <QQmlApplicationEngine> |
|||
#include <QtCore/QtCore> |
|||
#include <QDebug> |
|||
#include "StatusPane.h" |
|||
#include "QContractDefinition.h" |
|||
#include "AppContext.h" |
|||
#include "CodeModel.h" |
|||
#include <QClipboard> |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
StatusPane::StatusPane(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::HeaderView) |
|||
Clipboard::Clipboard() |
|||
{ |
|||
_context->appEngine()->rootContext()->setContextProperty("statusPane", this); |
|||
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();}); |
|||
} |
|||
|
|||
QString StatusPane::contentUrl() const |
|||
QString Clipboard::text() const |
|||
{ |
|||
return QStringLiteral("qrc:/qml/StatusPane.qml"); |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
return clipboard->text(); |
|||
} |
|||
|
|||
QString StatusPane::title() const |
|||
void Clipboard::setText(QString _text) |
|||
{ |
|||
return QApplication::tr("compiler"); |
|||
QClipboard *clipboard = QApplication::clipboard(); |
|||
clipboard->setText(_text); |
|||
} |
|||
|
|||
void StatusPane::start() const |
|||
{ |
|||
} |
|||
|
@ -1,88 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeEditorExtensionManager.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#include <QQuickItem> |
|||
#include <QGraphicsObject> |
|||
#include <QQmlEngine> |
|||
#include <QQmlComponent> |
|||
#include <QQuickTextDocument> |
|||
#include "StatusPane.h" |
|||
#include "AppContext.h" |
|||
#include "MixApplication.h" |
|||
#include "CodeModel.h" |
|||
#include "ClientModel.h" |
|||
#include "CodeHighlighter.h" |
|||
#include "CodeEditorExtensionManager.h" |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
CodeEditorExtensionManager::CodeEditorExtensionManager(): |
|||
m_appContext(static_cast<MixApplication*>(QApplication::instance())->context()) |
|||
{ |
|||
} |
|||
|
|||
CodeEditorExtensionManager::~CodeEditorExtensionManager() |
|||
{ |
|||
m_features.clear(); |
|||
} |
|||
|
|||
void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) |
|||
{ |
|||
if (!_editor) |
|||
return; |
|||
} |
|||
|
|||
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext) |
|||
{ |
|||
if (!_ext->contentUrl().isEmpty()) |
|||
{ |
|||
try |
|||
{ |
|||
if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::RightView) |
|||
_ext->addTabOn(m_rightView); |
|||
if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::HeaderView) |
|||
_ext->addTabOn(m_headerView); |
|||
} |
|||
catch (...) |
|||
{ |
|||
qDebug() << "Exception when adding tab into view."; |
|||
return; |
|||
} |
|||
} |
|||
_ext->start(); |
|||
m_features.append(_ext); |
|||
} |
|||
|
|||
void CodeEditorExtensionManager::applyCodeHighlight() |
|||
{ |
|||
//TODO: reimplement
|
|||
} |
|||
|
|||
void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView) |
|||
{ |
|||
m_rightView = _rightView; |
|||
} |
|||
|
|||
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) |
|||
{ |
|||
m_headerView = _headerView; |
|||
} |
@ -1,71 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CodeEditorExtensionManager.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <QQuickItem> |
|||
#include <QTextDocument> |
|||
#include <QVector> |
|||
#include "StatusPane.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
|
|||
class AppContext; |
|||
|
|||
/**
|
|||
* @brief Init and provides connection between extensions. |
|||
*/ |
|||
class CodeEditorExtensionManager: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(QQuickItem* headerView MEMBER m_headerView WRITE setHeaderView) |
|||
Q_PROPERTY(QQuickItem* rightView MEMBER m_rightView WRITE setRightView) |
|||
|
|||
public: |
|||
CodeEditorExtensionManager(); |
|||
~CodeEditorExtensionManager(); |
|||
/// Initialize extension.
|
|||
void initExtension(std::shared_ptr<Extension>); |
|||
/// Set current tab view
|
|||
void setHeaderView(QQuickItem*); |
|||
/// Set current right tab view.
|
|||
void setRightView(QQuickItem*); |
|||
|
|||
private slots: |
|||
void applyCodeHighlight(); |
|||
|
|||
private: |
|||
QVector<std::shared_ptr<Extension>> m_features; |
|||
QQuickItem* m_headerView; |
|||
QQuickItem* m_rightView; |
|||
AppContext* m_appContext; |
|||
void loadEditor(QQuickItem* _editor); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,82 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Extension.cpp
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
|
|||
#include <QMessageBox> |
|||
#include <QDebug> |
|||
#include <QQmlApplicationEngine> |
|||
|
|||
#include <libevm/VM.h> |
|||
#include <libwebthree/WebThree.h> |
|||
#include "Extension.h" |
|||
#include "AppContext.h" |
|||
|
|||
using namespace dev; |
|||
using namespace dev::mix; |
|||
|
|||
Extension::Extension(AppContext* _context) |
|||
{ |
|||
init(_context); |
|||
} |
|||
|
|||
Extension::Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior) |
|||
{ |
|||
init(_context); |
|||
m_displayBehavior = _displayBehavior; |
|||
} |
|||
|
|||
void Extension::init(AppContext* _context) |
|||
{ |
|||
m_ctx = _context; |
|||
m_appEngine = m_ctx->appEngine(); |
|||
} |
|||
|
|||
void Extension::addTabOn(QObject* _view) |
|||
{ |
|||
if (contentUrl() == "") |
|||
return; |
|||
|
|||
QVariant returnValue; |
|||
QQmlComponent* component = new QQmlComponent( |
|||
m_appEngine, |
|||
QUrl(contentUrl()), _view); |
|||
|
|||
QMetaObject::invokeMethod(_view, "addTab", |
|||
Q_RETURN_ARG(QVariant, returnValue), |
|||
Q_ARG(QVariant, this->title()), |
|||
Q_ARG(QVariant, QVariant::fromValue(component))); |
|||
|
|||
m_view = qvariant_cast<QObject*>(returnValue); |
|||
} |
|||
|
|||
void Extension::addContentOn(QObject* _view) |
|||
{ |
|||
Q_UNUSED(_view); |
|||
if (m_displayBehavior == ExtensionDisplayBehavior::ModalDialog) |
|||
{ |
|||
QQmlComponent* component = new QQmlComponent(m_appEngine, QUrl(contentUrl()), _view); |
|||
QObject* dialogWin = m_appEngine->rootObjects().at(0)->findChild<QObject*>("dialog", Qt::FindChildrenRecursively); |
|||
QObject* dialogWinComponent = m_appEngine->rootObjects().at(0)->findChild<QObject*>("modalDialogContent", Qt::FindChildrenRecursively); |
|||
dialogWinComponent->setProperty("sourceComponent", QVariant::fromValue(component)); |
|||
dialogWin->setProperty("title", title()); |
|||
QMetaObject::invokeMethod(dialogWin, "open"); |
|||
} |
|||
//TODO add more view type.
|
|||
} |
|||
|
@ -1,75 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Extension.h
|
|||
* @author Yann yann@ethdev.com |
|||
* @date 2014 |
|||
* Ethereum IDE client. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <QApplication> |
|||
#include <QQmlComponent> |
|||
|
|||
class QQmlApplicationEngine; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace mix |
|||
{ |
|||
|
|||
class AppContext; |
|||
|
|||
enum ExtensionDisplayBehavior |
|||
{ |
|||
HeaderView, |
|||
RightView, |
|||
ModalDialog |
|||
}; |
|||
|
|||
|
|||
class Extension: public QObject |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
Extension(AppContext* _context); |
|||
Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior); |
|||
/// Return the QML url of the view to display.
|
|||
virtual QString contentUrl() const { return ""; } |
|||
/// Return the title of this extension.
|
|||
virtual QString title() const { return ""; } |
|||
/// Initialize extension.
|
|||
virtual void start() const {} |
|||
/// Add the view define in contentUrl() in the _view QObject.
|
|||
void addContentOn(QObject* _view); |
|||
/// Add the view define in contentUrl() in the _view QObject (_view has to be a tab).
|
|||
void addTabOn(QObject* _view); |
|||
/// Modify the display behavior of this extension.
|
|||
void setDisplayBehavior(ExtensionDisplayBehavior _displayBehavior) { m_displayBehavior = _displayBehavior; } |
|||
/// Get the display behavior of thi extension.
|
|||
ExtensionDisplayBehavior getDisplayBehavior() { return m_displayBehavior; } |
|||
|
|||
protected: |
|||
QObject* m_view; |
|||
ExtensionDisplayBehavior m_displayBehavior; |
|||
AppContext* m_ctx; |
|||
QQmlApplicationEngine* m_appEngine; |
|||
|
|||
private: |
|||
void init(AppContext* _context); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,65 @@ |
|||
<RCC> |
|||
<qresource prefix="/"> |
|||
<file>qml/AlertMessageDialog.qml</file> |
|||
<file>qml/BasicMessage.qml</file> |
|||
<file>qml/BigIntValue.qml</file> |
|||
<file>qml/CallStack.qml</file> |
|||
<file>qml/CodeEditorStyle.qml</file> |
|||
<file>qml/CodeEditorView.qml</file> |
|||
<file>qml/CommonSeparator.qml</file> |
|||
<file>qml/ContractLibrary.qml</file> |
|||
<file>qml/DebugBasicInfo.qml</file> |
|||
<file>qml/DebugInfoList.qml</file> |
|||
<file>qml/Debugger.qml</file> |
|||
<file>qml/DebuggerPaneStyle.qml</file> |
|||
<file>qml/DefaultLabel.qml</file> |
|||
<file>qml/DefaultTextField.qml</file> |
|||
<file>qml/DeploymentDialog.qml</file> |
|||
<file>qml/Ether.qml</file> |
|||
<file>qml/EtherValue.qml</file> |
|||
<file>qml/FilesSection.qml</file> |
|||
<file>qml/ItemDelegateDataDump.qml</file> |
|||
<file>qml/LogsPane.qml</file> |
|||
<file>qml/LogsPaneStyle.qml</file> |
|||
<file>qml/MainContent.qml</file> |
|||
<file>qml/ModalDialog.qml</file> |
|||
<file>qml/NewProjectDialog.qml</file> |
|||
<file>qml/ProjectFilesStyle.qml</file> |
|||
<file>qml/ProjectList.qml</file> |
|||
<file>qml/ProjectModel.qml</file> |
|||
<file>qml/QBoolTypeView.qml</file> |
|||
<file>qml/QHashTypeView.qml</file> |
|||
<file>qml/QIntTypeView.qml</file> |
|||
<file>qml/QRealTypeView.qml</file> |
|||
<file>qml/QStringTypeView.qml</file> |
|||
<file>qml/QVariableDeclaration.qml</file> |
|||
<file>qml/QVariableDefinition.qml</file> |
|||
<file>qml/SourceSansProBold.qml</file> |
|||
<file>qml/SourceSansProLight.qml</file> |
|||
<file>qml/SourceSansProRegular.qml</file> |
|||
<file>qml/Splitter.qml</file> |
|||
<file>qml/StateDialog.qml</file> |
|||
<file>qml/StateDialogStyle.qml</file> |
|||
<file>qml/StateList.qml</file> |
|||
<file>qml/StateListModel.qml</file> |
|||
<file>qml/StateStyle.qml</file> |
|||
<file>qml/StatusPane.qml</file> |
|||
<file>qml/StatusPaneStyle.qml</file> |
|||
<file>qml/StepActionImage.qml</file> |
|||
<file>qml/StorageView.qml</file> |
|||
<file>qml/StructView.qml</file> |
|||
<file>qml/Style.qml</file> |
|||
<file>qml/TabStyle.qml</file> |
|||
<file>qml/TransactionDialog.qml</file> |
|||
<file>qml/TransactionLog.qml</file> |
|||
<file>qml/VariablesView.qml</file> |
|||
<file>qml/WebPreviewStyle.qml</file> |
|||
<file>qml/js/Debugger.js</file> |
|||
<file>qml/js/ErrorLocationFormater.js</file> |
|||
<file>qml/js/ProjectModel.js</file> |
|||
<file>qml/js/QEtherHelper.js</file> |
|||
<file>qml/js/TransactionHelper.js</file> |
|||
<file>qml/main.qml</file> |
|||
<file>qml/qmldir</file> |
|||
</qresource> |
|||
</RCC> |
@ -0,0 +1,14 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Controls 1.3 |
|||
|
|||
SplitView |
|||
{ |
|||
handleDelegate: Rectangle { |
|||
width: 4 |
|||
height: 4 |
|||
color: "#cccccc" |
|||
} |
|||
} |
|||
|
|||
|
|||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,87 @@ |
|||
(function(mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
return mod(exports); |
|||
if (typeof define == "function" && define.amd) // AMD
|
|||
return define(["exports"], mod); |
|||
mod(tern.comment || (tern.comment = {})); |
|||
})(function(exports) { |
|||
function isSpace(ch) { |
|||
return (ch < 14 && ch > 8) || ch === 32 || ch === 160; |
|||
} |
|||
|
|||
function onOwnLine(text, pos) { |
|||
for (; pos > 0; --pos) { |
|||
var ch = text.charCodeAt(pos - 1); |
|||
if (ch == 10) break; |
|||
if (!isSpace(ch)) return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// Gather comments directly before a function
|
|||
exports.commentsBefore = function(text, pos) { |
|||
var found = null, emptyLines = 0, topIsLineComment; |
|||
out: while (pos > 0) { |
|||
var prev = text.charCodeAt(pos - 1); |
|||
if (prev == 10) { |
|||
for (var scan = --pos, sawNonWS = false; scan > 0; --scan) { |
|||
prev = text.charCodeAt(scan - 1); |
|||
if (prev == 47 && text.charCodeAt(scan - 2) == 47) { |
|||
if (!onOwnLine(text, scan - 2)) break out; |
|||
var content = text.slice(scan, pos); |
|||
if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0]; |
|||
else (found || (found = [])).unshift(content); |
|||
topIsLineComment = true; |
|||
emptyLines = 0; |
|||
pos = scan - 2; |
|||
break; |
|||
} else if (prev == 10) { |
|||
if (!sawNonWS && ++emptyLines > 1) break out; |
|||
break; |
|||
} else if (!sawNonWS && !isSpace(prev)) { |
|||
sawNonWS = true; |
|||
} |
|||
} |
|||
} else if (prev == 47 && text.charCodeAt(pos - 2) == 42) { |
|||
for (var scan = pos - 2; scan > 1; --scan) { |
|||
if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) { |
|||
if (!onOwnLine(text, scan - 2)) break out; |
|||
(found || (found = [])).unshift(text.slice(scan, pos - 2)); |
|||
topIsLineComment = false; |
|||
emptyLines = 0; |
|||
break; |
|||
} |
|||
} |
|||
pos = scan - 2; |
|||
} else if (isSpace(prev)) { |
|||
--pos; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
return found; |
|||
}; |
|||
|
|||
exports.commentAfter = function(text, pos) { |
|||
while (pos < text.length) { |
|||
var next = text.charCodeAt(pos); |
|||
if (next == 47) { |
|||
var after = text.charCodeAt(pos + 1), end; |
|||
if (after == 47) // line comment
|
|||
end = text.indexOf("\n", pos + 2); |
|||
else if (after == 42) // block comment
|
|||
end = text.indexOf("*/", pos + 2); |
|||
else |
|||
return; |
|||
return text.slice(pos + 2, end < 0 ? text.length : end); |
|||
} else if (isSpace(next)) { |
|||
++pos; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
exports.ensureCommentsBefore = function(text, node) { |
|||
if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore; |
|||
return node.commentsBefore = exports.commentsBefore(text, node.start); |
|||
}; |
|||
}); |
@ -0,0 +1,547 @@ |
|||
// Type description parser
|
|||
//
|
|||
// Type description JSON files (such as ecma5.json and browser.json)
|
|||
// are used to
|
|||
//
|
|||
// A) describe types that come from native code
|
|||
//
|
|||
// B) to cheaply load the types for big libraries, or libraries that
|
|||
// can't be inferred well
|
|||
|
|||
(function(mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
return exports.init = mod; |
|||
if (typeof define == "function" && define.amd) // AMD
|
|||
return define({init: mod}); |
|||
tern.def = {init: mod}; |
|||
})(function(exports, infer) { |
|||
"use strict"; |
|||
|
|||
function hop(obj, prop) { |
|||
return Object.prototype.hasOwnProperty.call(obj, prop); |
|||
} |
|||
|
|||
var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) { |
|||
this.pos = start || 0; |
|||
this.spec = spec; |
|||
this.base = base; |
|||
this.forceNew = forceNew; |
|||
}; |
|||
TypeParser.prototype = { |
|||
eat: function(str) { |
|||
if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) { |
|||
this.pos += str.length; |
|||
return true; |
|||
} |
|||
}, |
|||
word: function(re) { |
|||
var word = "", ch, re = re || /[\w$]/; |
|||
while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; } |
|||
return word; |
|||
}, |
|||
error: function() { |
|||
throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")"); |
|||
}, |
|||
parseFnType: function(name, top) { |
|||
var args = [], names = []; |
|||
if (!this.eat(")")) for (var i = 0; ; ++i) { |
|||
var colon = this.spec.indexOf(": ", this.pos), argname; |
|||
if (colon != -1) { |
|||
argname = this.spec.slice(this.pos, colon); |
|||
if (/^[$\w?]+$/.test(argname)) |
|||
this.pos = colon + 2; |
|||
else |
|||
argname = null; |
|||
} |
|||
names.push(argname); |
|||
args.push(this.parseType()); |
|||
if (!this.eat(", ")) { |
|||
this.eat(")") || this.error(); |
|||
break; |
|||
} |
|||
} |
|||
var retType, computeRet, computeRetStart, fn; |
|||
if (this.eat(" -> ")) { |
|||
if (top && this.spec.indexOf("!", this.pos) > -1) { |
|||
retType = infer.ANull; |
|||
computeRetStart = this.pos; |
|||
computeRet = this.parseRetType(); |
|||
} else retType = this.parseType(); |
|||
} else retType = infer.ANull; |
|||
if (top && (fn = this.base)) |
|||
infer.Fn.call(this.base, name, infer.ANull, args, names, retType); |
|||
else |
|||
fn = new infer.Fn(name, infer.ANull, args, names, retType); |
|||
if (computeRet) fn.computeRet = computeRet; |
|||
if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos); |
|||
return fn; |
|||
}, |
|||
parseType: function(name, top) { |
|||
var type, union = false; |
|||
for (;;) { |
|||
var inner = this.parseTypeInner(name, top); |
|||
if (union) inner.propagate(union); |
|||
else type = inner; |
|||
if (!this.eat("|")) break; |
|||
if (!union) { |
|||
union = new infer.AVal; |
|||
type.propagate(union); |
|||
type = union; |
|||
} |
|||
} |
|||
return type; |
|||
}, |
|||
parseTypeInner: function(name, top) { |
|||
if (this.eat("fn(")) { |
|||
return this.parseFnType(name, top); |
|||
} else if (this.eat("[")) { |
|||
var inner = this.parseType(); |
|||
this.eat("]") || this.error(); |
|||
if (top && this.base) { |
|||
infer.Arr.call(this.base, inner); |
|||
return this.base; |
|||
} |
|||
return new infer.Arr(inner); |
|||
} else if (this.eat("+")) { |
|||
var path = this.word(/[\w$<>\.!]/); |
|||
var base = parsePath(path + ".prototype"); |
|||
if (!(base instanceof infer.Obj)) base = parsePath(path); |
|||
if (!(base instanceof infer.Obj)) return base; |
|||
if (top && this.forceNew) return new infer.Obj(base); |
|||
return infer.getInstance(base); |
|||
} else if (this.eat("?")) { |
|||
return infer.ANull; |
|||
} else { |
|||
return this.fromWord(this.word(/[\w$<>\.!`]/)); |
|||
} |
|||
}, |
|||
fromWord: function(spec) { |
|||
var cx = infer.cx(); |
|||
switch (spec) { |
|||
case "number": return cx.num; |
|||
case "string": return cx.str; |
|||
case "bool": return cx.bool; |
|||
case "<top>": return cx.topScope; |
|||
} |
|||
if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec]; |
|||
return parsePath(spec); |
|||
}, |
|||
parseBaseRetType: function() { |
|||
if (this.eat("[")) { |
|||
var inner = this.parseRetType(); |
|||
this.eat("]") || this.error(); |
|||
return function(self, args) { return new infer.Arr(inner(self, args)); }; |
|||
} else if (this.eat("+")) { |
|||
var base = this.parseRetType(); |
|||
var result = function(self, args) { |
|||
var proto = base(self, args); |
|||
if (proto instanceof infer.Fn && proto.hasProp("prototype")) |
|||
proto = proto.getProp("prototype").getObjType(); |
|||
if (!(proto instanceof infer.Obj)) return proto; |
|||
return new infer.Obj(proto); |
|||
}; |
|||
if (this.eat("[")) return this.parsePoly(result); |
|||
return result; |
|||
} else if (this.eat("!")) { |
|||
var arg = this.word(/\d/); |
|||
if (arg) { |
|||
arg = Number(arg); |
|||
return function(_self, args) {return args[arg] || infer.ANull;}; |
|||
} else if (this.eat("this")) { |
|||
return function(self) {return self;}; |
|||
} else if (this.eat("custom:")) { |
|||
var fname = this.word(/[\w$]/); |
|||
return customFunctions[fname] || function() { return infer.ANull; }; |
|||
} else { |
|||
return this.fromWord("!" + arg + this.word(/[\w$<>\.!]/)); |
|||
} |
|||
} |
|||
var t = this.parseType(); |
|||
return function(){return t;}; |
|||
}, |
|||
extendRetType: function(base) { |
|||
var propName = this.word(/[\w<>$!]/) || this.error(); |
|||
if (propName == "!ret") return function(self, args) { |
|||
var lhs = base(self, args); |
|||
if (lhs.retval) return lhs.retval; |
|||
var rv = new infer.AVal; |
|||
lhs.propagate(new infer.IsCallee(infer.ANull, [], null, rv)); |
|||
return rv; |
|||
}; |
|||
return function(self, args) {return base(self, args).getProp(propName);}; |
|||
}, |
|||
parsePoly: function(base) { |
|||
var propName = "<i>", match; |
|||
if (match = this.spec.slice(this.pos).match(/^\s*(\w+)\s*=\s*/)) { |
|||
propName = match[1]; |
|||
this.pos += match[0].length; |
|||
} |
|||
var value = this.parseRetType(); |
|||
if (!this.eat("]")) this.error(); |
|||
return function(self, args) { |
|||
var instance = base(self, args); |
|||
if (instance instanceof infer.Obj) |
|||
value(self, args).propagate(instance.defProp(propName)); |
|||
return instance; |
|||
}; |
|||
}, |
|||
parseRetType: function() { |
|||
var tp = this.parseBaseRetType(); |
|||
while (this.eat(".")) tp = this.extendRetType(tp); |
|||
return tp; |
|||
} |
|||
}; |
|||
|
|||
function parseType(spec, name, base, forceNew) { |
|||
var type = new TypeParser(spec, null, base, forceNew).parseType(name, true); |
|||
if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) { |
|||
var arg = type.args[i]; |
|||
if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) { |
|||
var fArg = fArgs[i]; |
|||
if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull)); |
|||
}); |
|||
})(i); |
|||
return type; |
|||
} |
|||
|
|||
function addEffect(fn, handler, replaceRet) { |
|||
var oldCmp = fn.computeRet, rv = fn.retval; |
|||
fn.computeRet = function(self, args, argNodes) { |
|||
var handled = handler(self, args, argNodes); |
|||
var old = oldCmp ? oldCmp(self, args, argNodes) : rv; |
|||
return replaceRet ? handled : old; |
|||
}; |
|||
} |
|||
|
|||
var parseEffect = exports.parseEffect = function(effect, fn) { |
|||
var m; |
|||
if (effect.indexOf("propagate ") == 0) { |
|||
var p = new TypeParser(effect, 10); |
|||
var getOrigin = p.parseRetType(); |
|||
if (!p.eat(" ")) p.error(); |
|||
var getTarget = p.parseRetType(); |
|||
addEffect(fn, function(self, args) { |
|||
getOrigin(self, args).propagate(getTarget(self, args)); |
|||
}); |
|||
} else if (effect.indexOf("call ") == 0) { |
|||
var andRet = effect.indexOf("and return ", 5) == 5; |
|||
var p = new TypeParser(effect, andRet ? 16 : 5); |
|||
var getCallee = p.parseRetType(), getSelf = null, getArgs = []; |
|||
if (p.eat(" this=")) getSelf = p.parseRetType(); |
|||
while (p.eat(" ")) getArgs.push(p.parseRetType()); |
|||
addEffect(fn, function(self, args) { |
|||
var callee = getCallee(self, args); |
|||
var slf = getSelf ? getSelf(self, args) : infer.ANull, as = []; |
|||
for (var i = 0; i < getArgs.length; ++i) as.push(getArgs[i](self, args)); |
|||
var result = andRet ? new infer.AVal : infer.ANull; |
|||
callee.propagate(new infer.IsCallee(slf, as, null, result)); |
|||
return result; |
|||
}, andRet); |
|||
} else if (m = effect.match(/^custom (\S+)\s*(.*)/)) { |
|||
var customFunc = customFunctions[m[1]]; |
|||
if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc); |
|||
} else if (effect.indexOf("copy ") == 0) { |
|||
var p = new TypeParser(effect, 5); |
|||
var getFrom = p.parseRetType(); |
|||
p.eat(" "); |
|||
var getTo = p.parseRetType(); |
|||
addEffect(fn, function(self, args) { |
|||
var from = getFrom(self, args), to = getTo(self, args); |
|||
from.forAllProps(function(prop, val, local) { |
|||
if (local && prop != "<i>") |
|||
to.propagate(new infer.PropHasSubset(prop, val)); |
|||
}); |
|||
}); |
|||
} else { |
|||
throw new Error("Unknown effect type: " + effect); |
|||
} |
|||
}; |
|||
|
|||
var currentTopScope; |
|||
|
|||
var parsePath = exports.parsePath = function(path, scope) { |
|||
var cx = infer.cx(), cached = cx.paths[path], origPath = path; |
|||
if (cached != null) return cached; |
|||
cx.paths[path] = infer.ANull; |
|||
|
|||
var base = scope || currentTopScope || cx.topScope; |
|||
|
|||
if (cx.localDefs) for (var name in cx.localDefs) { |
|||
if (path.indexOf(name) == 0) { |
|||
if (path == name) return cx.paths[path] = cx.localDefs[path]; |
|||
if (path.charAt(name.length) == ".") { |
|||
base = cx.localDefs[name]; |
|||
path = path.slice(name.length + 1); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
var parts = path.split("."); |
|||
for (var i = 0; i < parts.length && base != infer.ANull; ++i) { |
|||
var prop = parts[i]; |
|||
if (prop.charAt(0) == "!") { |
|||
if (prop == "!proto") { |
|||
base = (base instanceof infer.Obj && base.proto) || infer.ANull; |
|||
} else { |
|||
var fn = base.getFunctionType(); |
|||
if (!fn) { |
|||
base = infer.ANull; |
|||
} else if (prop == "!ret") { |
|||
base = fn.retval && fn.retval.getType(false) || infer.ANull; |
|||
} else { |
|||
var arg = fn.args && fn.args[Number(prop.slice(1))]; |
|||
base = (arg && arg.getType(false)) || infer.ANull; |
|||
} |
|||
} |
|||
} else if (base instanceof infer.Obj) { |
|||
var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop]; |
|||
if (!propVal || propVal.isEmpty()) |
|||
base = infer.ANull; |
|||
else |
|||
base = propVal.types[0]; |
|||
} |
|||
} |
|||
// Uncomment this to get feedback on your poorly written .json files
|
|||
// if (base == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")");
|
|||
cx.paths[origPath] = base == infer.ANull ? null : base; |
|||
return base; |
|||
}; |
|||
|
|||
function emptyObj(ctor) { |
|||
var empty = Object.create(ctor.prototype); |
|||
empty.props = Object.create(null); |
|||
empty.isShell = true; |
|||
return empty; |
|||
} |
|||
|
|||
function isSimpleAnnotation(spec) { |
|||
if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false; |
|||
for (var prop in spec) |
|||
if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data") |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
function passOne(base, spec, path) { |
|||
if (!base) { |
|||
var tp = spec["!type"]; |
|||
if (tp) { |
|||
if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn); |
|||
else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr); |
|||
else throw new Error("Invalid !type spec: " + tp); |
|||
} else if (spec["!stdProto"]) { |
|||
base = infer.cx().protos[spec["!stdProto"]]; |
|||
} else { |
|||
base = emptyObj(infer.Obj); |
|||
} |
|||
base.name = path; |
|||
} |
|||
|
|||
for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) { |
|||
var inner = spec[name]; |
|||
if (typeof inner == "string" || isSimpleAnnotation(inner)) continue; |
|||
var prop = base.defProp(name); |
|||
passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop); |
|||
} |
|||
return base; |
|||
} |
|||
|
|||
function passTwo(base, spec, path) { |
|||
if (base.isShell) { |
|||
delete base.isShell; |
|||
var tp = spec["!type"]; |
|||
if (tp) { |
|||
parseType(tp, path, base); |
|||
} else { |
|||
var proto = spec["!proto"] && parseType(spec["!proto"]); |
|||
infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path); |
|||
} |
|||
} |
|||
|
|||
var effects = spec["!effects"]; |
|||
if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i) |
|||
parseEffect(effects[i], base); |
|||
copyInfo(spec, base); |
|||
|
|||
for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) { |
|||
var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name; |
|||
if (typeof inner == "string") { |
|||
if (known.isEmpty()) parseType(inner, innerPath).propagate(known); |
|||
} else { |
|||
if (!isSimpleAnnotation(inner)) |
|||
passTwo(known.getObjType(), inner, innerPath); |
|||
else if (known.isEmpty()) |
|||
parseType(inner["!type"], innerPath, null, true).propagate(known); |
|||
else |
|||
continue; |
|||
if (inner["!doc"]) known.doc = inner["!doc"]; |
|||
if (inner["!url"]) known.url = inner["!url"]; |
|||
if (inner["!span"]) known.span = inner["!span"]; |
|||
} |
|||
} |
|||
return base; |
|||
} |
|||
|
|||
function copyInfo(spec, type) { |
|||
if (spec["!doc"]) type.doc = spec["!doc"]; |
|||
if (spec["!url"]) type.url = spec["!url"]; |
|||
if (spec["!span"]) type.span = spec["!span"]; |
|||
if (spec["!data"]) type.metaData = spec["!data"]; |
|||
} |
|||
|
|||
function runPasses(type, arg) { |
|||
var parent = infer.cx().parent, pass = parent && parent.passes && parent.passes[type]; |
|||
if (pass) for (var i = 0; i < pass.length; i++) pass[i](arg); |
|||
} |
|||
|
|||
function doLoadEnvironment(data, scope) { |
|||
var cx = infer.cx(); |
|||
|
|||
infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length); |
|||
cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null); |
|||
|
|||
runPasses("preLoadDef", data); |
|||
|
|||
passOne(scope, data); |
|||
|
|||
var def = data["!define"]; |
|||
if (def) { |
|||
for (var name in def) { |
|||
var spec = def[name]; |
|||
cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name); |
|||
} |
|||
for (var name in def) { |
|||
var spec = def[name]; |
|||
if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name); |
|||
} |
|||
} |
|||
|
|||
passTwo(scope, data); |
|||
|
|||
runPasses("postLoadDef", data); |
|||
|
|||
cx.curOrigin = cx.localDefs = null; |
|||
} |
|||
|
|||
exports.load = function(data, scope) { |
|||
if (!scope) scope = infer.cx().topScope; |
|||
var oldScope = currentTopScope; |
|||
currentTopScope = scope; |
|||
try { |
|||
doLoadEnvironment(data, scope); |
|||
} finally { |
|||
currentTopScope = oldScope; |
|||
} |
|||
}; |
|||
|
|||
exports.parse = function(data, origin, path) { |
|||
var cx = infer.cx(); |
|||
if (origin) { |
|||
cx.origin = origin; |
|||
cx.localDefs = cx.definitions[origin]; |
|||
} |
|||
|
|||
try { |
|||
if (typeof data == "string") |
|||
return parseType(data, path); |
|||
else |
|||
return passTwo(passOne(null, data, path), data, path); |
|||
} finally { |
|||
if (origin) cx.origin = cx.localDefs = null; |
|||
} |
|||
}; |
|||
|
|||
// Used to register custom logic for more involved effect or type
|
|||
// computation.
|
|||
var customFunctions = Object.create(null); |
|||
infer.registerFunction = function(name, f) { customFunctions[name] = f; }; |
|||
|
|||
var IsCreated = infer.constraint("created, target, spec", { |
|||
addType: function(tp) { |
|||
if (tp instanceof infer.Obj && this.created++ < 5) { |
|||
var derived = new infer.Obj(tp), spec = this.spec; |
|||
if (spec instanceof infer.AVal) spec = spec.getObjType(false); |
|||
if (spec instanceof infer.Obj) for (var prop in spec.props) { |
|||
var cur = spec.props[prop].types[0]; |
|||
var p = derived.defProp(prop); |
|||
if (cur && cur instanceof infer.Obj && cur.props.value) { |
|||
var vtp = cur.props.value.getType(false); |
|||
if (vtp) p.addType(vtp); |
|||
} |
|||
} |
|||
this.target.addType(derived); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
infer.registerFunction("Object_create", function(_self, args, argNodes) { |
|||
if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null) |
|||
return new infer.Obj(); |
|||
|
|||
var result = new infer.AVal; |
|||
if (args[0]) args[0].propagate(new IsCreated(0, result, args[1])); |
|||
return result; |
|||
}); |
|||
|
|||
var PropSpec = infer.constraint("target", { |
|||
addType: function(tp) { |
|||
if (!(tp instanceof infer.Obj)) return; |
|||
if (tp.hasProp("value")) |
|||
tp.getProp("value").propagate(this.target); |
|||
else if (tp.hasProp("get")) |
|||
tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target)); |
|||
} |
|||
}); |
|||
|
|||
infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) { |
|||
if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" && |
|||
typeof argNodes[1].value == "string") { |
|||
var obj = args[0], connect = new infer.AVal; |
|||
obj.propagate(new infer.PropHasSubset(argNodes[1].value, connect, argNodes[1])); |
|||
args[2].propagate(new PropSpec(connect)); |
|||
} |
|||
return infer.ANull; |
|||
}); |
|||
|
|||
var IsBound = infer.constraint("self, args, target", { |
|||
addType: function(tp) { |
|||
if (!(tp instanceof infer.Fn)) return; |
|||
this.target.addType(new infer.Fn(tp.name, tp.self, tp.args.slice(this.args.length), |
|||
tp.argNames.slice(this.args.length), tp.retval)); |
|||
this.self.propagate(tp.self); |
|||
for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i) |
|||
this.args[i].propagate(tp.args[i]); |
|||
} |
|||
}); |
|||
|
|||
infer.registerFunction("Function_bind", function(self, args) { |
|||
if (!args.length) return infer.ANull; |
|||
var result = new infer.AVal; |
|||
self.propagate(new IsBound(args[0], args.slice(1), result)); |
|||
return result; |
|||
}); |
|||
|
|||
infer.registerFunction("Array_ctor", function(_self, args) { |
|||
var arr = new infer.Arr; |
|||
if (args.length != 1 || !args[0].hasType(infer.cx().num)) { |
|||
var content = arr.getProp("<i>"); |
|||
for (var i = 0; i < args.length; ++i) args[i].propagate(content); |
|||
} |
|||
return arr; |
|||
}); |
|||
|
|||
infer.registerFunction("Promise_ctor", function(_self, args, argNodes) { |
|||
if (args.length < 1) return infer.ANull; |
|||
var self = new infer.Obj(infer.cx().definitions.ecma6["Promise.prototype"]); |
|||
var valProp = self.defProp("value", argNodes && argNodes[0]); |
|||
var valArg = new infer.AVal; |
|||
valArg.propagate(valProp); |
|||
var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull); |
|||
var reject = infer.cx().definitions.ecma6.promiseReject; |
|||
args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull)); |
|||
return self; |
|||
}); |
|||
|
|||
return exports; |
|||
}); |
@ -0,0 +1,402 @@ |
|||
// Parses comments above variable declarations, function declarations,
|
|||
// and object properties as docstrings and JSDoc-style type
|
|||
// annotations.
|
|||
|
|||
(function(mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/comment"), |
|||
require("acorn/acorn"), require("acorn/util/walk")); |
|||
if (typeof define == "function" && define.amd) // AMD
|
|||
return define(["../lib/infer", "../lib/tern", "../lib/comment", "acorn/acorn", "acorn/util/walk"], mod); |
|||
mod(tern, tern, tern.comment, acorn, acorn.walk); |
|||
})(function(infer, tern, comment, acorn, walk) { |
|||
"use strict"; |
|||
|
|||
var WG_MADEUP = 1, WG_STRONG = 101; |
|||
|
|||
tern.registerPlugin("doc_comment", function(server, options) { |
|||
server.jsdocTypedefs = Object.create(null); |
|||
server.on("reset", function() { |
|||
server.jsdocTypedefs = Object.create(null); |
|||
}); |
|||
server._docComment = { |
|||
weight: options && options.strong ? WG_STRONG : undefined, |
|||
fullDocs: options && options.fullDocs |
|||
}; |
|||
|
|||
return { |
|||
passes: { |
|||
postParse: postParse, |
|||
postInfer: postInfer, |
|||
postLoadDef: postLoadDef |
|||
} |
|||
}; |
|||
}); |
|||
|
|||
function postParse(ast, text) { |
|||
function attachComments(node) { comment.ensureCommentsBefore(text, node); } |
|||
|
|||
walk.simple(ast, { |
|||
VariableDeclaration: attachComments, |
|||
FunctionDeclaration: attachComments, |
|||
AssignmentExpression: function(node) { |
|||
if (node.operator == "=") attachComments(node); |
|||
}, |
|||
ObjectExpression: function(node) { |
|||
for (var i = 0; i < node.properties.length; ++i) |
|||
attachComments(node.properties[i]); |
|||
}, |
|||
CallExpression: function(node) { |
|||
if (isDefinePropertyCall(node)) attachComments(node); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function isDefinePropertyCall(node) { |
|||
return node.callee.type == "MemberExpression" && |
|||
node.callee.object.name == "Object" && |
|||
node.callee.property.name == "defineProperty" && |
|||
node.arguments.length >= 3 && |
|||
typeof node.arguments[1].value == "string"; |
|||
} |
|||
|
|||
function postInfer(ast, scope) { |
|||
jsdocParseTypedefs(ast.sourceFile.text, scope); |
|||
|
|||
walk.simple(ast, { |
|||
VariableDeclaration: function(node, scope) { |
|||
if (node.commentsBefore) |
|||
interpretComments(node, node.commentsBefore, scope, |
|||
scope.getProp(node.declarations[0].id.name)); |
|||
}, |
|||
FunctionDeclaration: function(node, scope) { |
|||
if (node.commentsBefore) |
|||
interpretComments(node, node.commentsBefore, scope, |
|||
scope.getProp(node.id.name), |
|||
node.body.scope.fnType); |
|||
}, |
|||
AssignmentExpression: function(node, scope) { |
|||
if (node.commentsBefore) |
|||
interpretComments(node, node.commentsBefore, scope, |
|||
infer.expressionType({node: node.left, state: scope})); |
|||
}, |
|||
ObjectExpression: function(node, scope) { |
|||
for (var i = 0; i < node.properties.length; ++i) { |
|||
var prop = node.properties[i]; |
|||
if (prop.commentsBefore) |
|||
interpretComments(prop, prop.commentsBefore, scope, |
|||
node.objType.getProp(prop.key.name)); |
|||
} |
|||
}, |
|||
CallExpression: function(node, scope) { |
|||
if (node.commentsBefore && isDefinePropertyCall(node)) { |
|||
var type = infer.expressionType({node: node.arguments[0], state: scope}).getObjType(); |
|||
if (type && type instanceof infer.Obj) { |
|||
var prop = type.props[node.arguments[1].value]; |
|||
if (prop) interpretComments(node, node.commentsBefore, scope, prop); |
|||
} |
|||
} |
|||
} |
|||
}, infer.searchVisitor, scope); |
|||
} |
|||
|
|||
function postLoadDef(data) { |
|||
var defs = data["!typedef"]; |
|||
var cx = infer.cx(), orig = data["!name"]; |
|||
if (defs) for (var name in defs) |
|||
cx.parent.jsdocTypedefs[name] = |
|||
maybeInstance(infer.def.parse(defs[name], orig, name), name); |
|||
} |
|||
|
|||
// COMMENT INTERPRETATION
|
|||
|
|||
function interpretComments(node, comments, scope, aval, type) { |
|||
jsdocInterpretComments(node, scope, aval, comments); |
|||
var cx = infer.cx(); |
|||
|
|||
if (!type && aval instanceof infer.AVal && aval.types.length) { |
|||
type = aval.types[aval.types.length - 1]; |
|||
if (!(type instanceof infer.Obj) || type.origin != cx.curOrigin || type.doc) |
|||
type = null; |
|||
} |
|||
|
|||
var result = comments[comments.length - 1]; |
|||
if (cx.parent._docComment.fullDocs) { |
|||
result = result.trim().replace(/\n[ \t]*\* ?/g, "\n"); |
|||
} else { |
|||
var dot = result.search(/\.\s/); |
|||
if (dot > 5) result = result.slice(0, dot + 1); |
|||
result = result.trim().replace(/\s*\n\s*\*\s*|\s{1,}/g, " "); |
|||
} |
|||
result = result.replace(/^\s*\*+\s*/, ""); |
|||
|
|||
if (aval instanceof infer.AVal) aval.doc = result; |
|||
if (type) type.doc = result; |
|||
} |
|||
|
|||
// Parses a subset of JSDoc-style comments in order to include the
|
|||
// explicitly defined types in the analysis.
|
|||
|
|||
function skipSpace(str, pos) { |
|||
while (/\s/.test(str.charAt(pos))) ++pos; |
|||
return pos; |
|||
} |
|||
|
|||
function isIdentifier(string) { |
|||
if (!acorn.isIdentifierStart(string.charCodeAt(0))) return false; |
|||
for (var i = 1; i < string.length; i++) |
|||
if (!acorn.isIdentifierChar(string.charCodeAt(i))) return false; |
|||
return true; |
|||
} |
|||
|
|||
function parseLabelList(scope, str, pos, close) { |
|||
var labels = [], types = [], madeUp = false; |
|||
for (var first = true; ; first = false) { |
|||
pos = skipSpace(str, pos); |
|||
if (first && str.charAt(pos) == close) break; |
|||
var colon = str.indexOf(":", pos); |
|||
if (colon < 0) return null; |
|||
var label = str.slice(pos, colon); |
|||
if (!isIdentifier(label)) return null; |
|||
labels.push(label); |
|||
pos = colon + 1; |
|||
var type = parseType(scope, str, pos); |
|||
if (!type) return null; |
|||
pos = type.end; |
|||
madeUp = madeUp || type.madeUp; |
|||
types.push(type.type); |
|||
pos = skipSpace(str, pos); |
|||
var next = str.charAt(pos); |
|||
++pos; |
|||
if (next == close) break; |
|||
if (next != ",") return null; |
|||
} |
|||
return {labels: labels, types: types, end: pos, madeUp: madeUp}; |
|||
} |
|||
|
|||
function parseType(scope, str, pos) { |
|||
var type, union = false, madeUp = false; |
|||
for (;;) { |
|||
var inner = parseTypeInner(scope, str, pos); |
|||
if (!inner) return null; |
|||
madeUp = madeUp || inner.madeUp; |
|||
if (union) inner.type.propagate(union); |
|||
else type = inner.type; |
|||
pos = skipSpace(str, inner.end); |
|||
if (str.charAt(pos) != "|") break; |
|||
pos++; |
|||
if (!union) { |
|||
union = new infer.AVal; |
|||
type.propagate(union); |
|||
type = union; |
|||
} |
|||
} |
|||
var isOptional = false; |
|||
if (str.charAt(pos) == "=") { |
|||
++pos; |
|||
isOptional = true; |
|||
} |
|||
return {type: type, end: pos, isOptional: isOptional, madeUp: madeUp}; |
|||
} |
|||
|
|||
function parseTypeInner(scope, str, pos) { |
|||
pos = skipSpace(str, pos); |
|||
var type, madeUp = false; |
|||
|
|||
if (str.indexOf("function(", pos) == pos) { |
|||
var args = parseLabelList(scope, str, pos + 9, ")"), ret = infer.ANull; |
|||
if (!args) return null; |
|||
pos = skipSpace(str, args.end); |
|||
if (str.charAt(pos) == ":") { |
|||
++pos; |
|||
var retType = parseType(scope, str, pos + 1); |
|||
if (!retType) return null; |
|||
pos = retType.end; |
|||
ret = retType.type; |
|||
madeUp = retType.madeUp; |
|||
} |
|||
type = new infer.Fn(null, infer.ANull, args.types, args.labels, ret); |
|||
} else if (str.charAt(pos) == "[") { |
|||
var inner = parseType(scope, str, pos + 1); |
|||
if (!inner) return null; |
|||
pos = skipSpace(str, inner.end); |
|||
madeUp = inner.madeUp; |
|||
if (str.charAt(pos) != "]") return null; |
|||
++pos; |
|||
type = new infer.Arr(inner.type); |
|||
} else if (str.charAt(pos) == "{") { |
|||
var fields = parseLabelList(scope, str, pos + 1, "}"); |
|||
if (!fields) return null; |
|||
type = new infer.Obj(true); |
|||
for (var i = 0; i < fields.types.length; ++i) { |
|||
var field = type.defProp(fields.labels[i]); |
|||
field.initializer = true; |
|||
fields.types[i].propagate(field); |
|||
} |
|||
pos = fields.end; |
|||
madeUp = fields.madeUp; |
|||
} else if (str.charAt(pos) == "(") { |
|||
var inner = parseType(scope, str, pos + 1); |
|||
if (!inner) return null; |
|||
pos = skipSpace(str, inner.end); |
|||
if (str.charAt(pos) != ")") return null; |
|||
++pos; |
|||
type = inner.type; |
|||
} else { |
|||
var start = pos; |
|||
if (!acorn.isIdentifierStart(str.charCodeAt(pos))) return null; |
|||
while (acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos; |
|||
if (start == pos) return null; |
|||
var word = str.slice(start, pos); |
|||
if (/^(number|integer)$/i.test(word)) type = infer.cx().num; |
|||
else if (/^bool(ean)?$/i.test(word)) type = infer.cx().bool; |
|||
else if (/^string$/i.test(word)) type = infer.cx().str; |
|||
else if (/^(null|undefined)$/i.test(word)) type = infer.ANull; |
|||
else if (/^array$/i.test(word)) { |
|||
var inner = null; |
|||
if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") { |
|||
var inAngles = parseType(scope, str, pos + 2); |
|||
if (!inAngles) return null; |
|||
pos = skipSpace(str, inAngles.end); |
|||
madeUp = inAngles.madeUp; |
|||
if (str.charAt(pos++) != ">") return null; |
|||
inner = inAngles.type; |
|||
} |
|||
type = new infer.Arr(inner); |
|||
} else if (/^object$/i.test(word)) { |
|||
type = new infer.Obj(true); |
|||
if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") { |
|||
var key = parseType(scope, str, pos + 2); |
|||
if (!key) return null; |
|||
pos = skipSpace(str, key.end); |
|||
madeUp = madeUp || key.madeUp; |
|||
if (str.charAt(pos++) != ",") return null; |
|||
var val = parseType(scope, str, pos); |
|||
if (!val) return null; |
|||
pos = skipSpace(str, val.end); |
|||
madeUp = key.madeUp || val.madeUp; |
|||
if (str.charAt(pos++) != ">") return null; |
|||
val.type.propagate(type.defProp("<i>")); |
|||
} |
|||
} else { |
|||
while (str.charCodeAt(pos) == 46 || |
|||
acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos; |
|||
var path = str.slice(start, pos); |
|||
var cx = infer.cx(), defs = cx.parent && cx.parent.jsdocTypedefs, found; |
|||
if (defs && (path in defs)) { |
|||
type = defs[path]; |
|||
} else if (found = infer.def.parsePath(path, scope).getObjType()) { |
|||
type = maybeInstance(found, path); |
|||
} else { |
|||
if (!cx.jsdocPlaceholders) cx.jsdocPlaceholders = Object.create(null); |
|||
if (!(path in cx.jsdocPlaceholders)) |
|||
type = cx.jsdocPlaceholders[path] = new infer.Obj(null, path); |
|||
else |
|||
type = cx.jsdocPlaceholders[path]; |
|||
madeUp = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return {type: type, end: pos, madeUp: madeUp}; |
|||
} |
|||
|
|||
function maybeInstance(type, path) { |
|||
if (type instanceof infer.Fn && /^[A-Z]/.test(path)) { |
|||
var proto = type.getProp("prototype").getObjType(); |
|||
if (proto instanceof infer.Obj) return infer.getInstance(proto); |
|||
} |
|||
return type; |
|||
} |
|||
|
|||
function parseTypeOuter(scope, str, pos) { |
|||
pos = skipSpace(str, pos || 0); |
|||
if (str.charAt(pos) != "{") return null; |
|||
var result = parseType(scope, str, pos + 1); |
|||
if (!result) return null; |
|||
var end = skipSpace(str, result.end); |
|||
if (str.charAt(end) != "}") return null; |
|||
result.end = end + 1; |
|||
return result; |
|||
} |
|||
|
|||
function jsdocInterpretComments(node, scope, aval, comments) { |
|||
var type, args, ret, foundOne, self, parsed; |
|||
|
|||
for (var i = 0; i < comments.length; ++i) { |
|||
var comment = comments[i]; |
|||
var decl = /(?:\n|$|\*)\s*@(type|param|arg(?:ument)?|returns?|this)\s+(.*)/g, m; |
|||
while (m = decl.exec(comment)) { |
|||
if (m[1] == "this" && (parsed = parseType(scope, m[2], 0))) { |
|||
self = parsed; |
|||
foundOne = true; |
|||
continue; |
|||
} |
|||
|
|||
if (!(parsed = parseTypeOuter(scope, m[2]))) continue; |
|||
foundOne = true; |
|||
|
|||
switch(m[1]) { |
|||
case "returns": case "return": |
|||
ret = parsed; break; |
|||
case "type": |
|||
type = parsed; break; |
|||
case "param": case "arg": case "argument": |
|||
var name = m[2].slice(parsed.end).match(/^\s*(\[?)\s*([^\]\s=]+)\s*(?:=[^\]]+\s*)?(\]?).*/); |
|||
if (!name) continue; |
|||
var argname = name[2] + (parsed.isOptional || (name[1] === '[' && name[3] === ']') ? "?" : ""); |
|||
(args || (args = Object.create(null)))[argname] = parsed; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (foundOne) applyType(type, self, args, ret, node, aval); |
|||
}; |
|||
|
|||
function jsdocParseTypedefs(text, scope) { |
|||
var cx = infer.cx(); |
|||
|
|||
var re = /\s@typedef\s+(.*)/g, m; |
|||
while (m = re.exec(text)) { |
|||
var parsed = parseTypeOuter(scope, m[1]); |
|||
var name = parsed && m[1].slice(parsed.end).match(/^\s*(\S+)/); |
|||
if (name) |
|||
cx.parent.jsdocTypedefs[name[1]] = parsed.type; |
|||
} |
|||
} |
|||
|
|||
function propagateWithWeight(type, target) { |
|||
var weight = infer.cx().parent._docComment.weight; |
|||
type.type.propagate(target, weight || (type.madeUp ? WG_MADEUP : undefined)); |
|||
} |
|||
|
|||
function applyType(type, self, args, ret, node, aval) { |
|||
var fn; |
|||
if (node.type == "VariableDeclaration") { |
|||
var decl = node.declarations[0]; |
|||
if (decl.init && decl.init.type == "FunctionExpression") fn = decl.init.body.scope.fnType; |
|||
} else if (node.type == "FunctionDeclaration") { |
|||
fn = node.body.scope.fnType; |
|||
} else if (node.type == "AssignmentExpression") { |
|||
if (node.right.type == "FunctionExpression") |
|||
fn = node.right.body.scope.fnType; |
|||
} else if (node.type == "CallExpression") { |
|||
} else { // An object property
|
|||
if (node.value.type == "FunctionExpression") fn = node.value.body.scope.fnType; |
|||
} |
|||
|
|||
if (fn && (args || ret || self)) { |
|||
if (args) for (var i = 0; i < fn.argNames.length; ++i) { |
|||
var name = fn.argNames[i], known = args[name]; |
|||
if (!known && (known = args[name + "?"])) |
|||
fn.argNames[i] += "?"; |
|||
if (known) propagateWithWeight(known, fn.args[i]); |
|||
} |
|||
if (ret) propagateWithWeight(ret, fn.retval); |
|||
if (self) propagateWithWeight(self, fn.self); |
|||
} else if (type) { |
|||
propagateWithWeight(type, aval); |
|||
} |
|||
}; |
|||
}); |
@ -0,0 +1,969 @@ |
|||
function ecma5Spec() |
|||
{ |
|||
return { |
|||
"!name": "ecma5", |
|||
"!define": {"Error.prototype": "Error.prototype"}, |
|||
"Infinity": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Infinity", |
|||
"!doc": "A numeric value representing infinity." |
|||
}, |
|||
"undefined": { |
|||
"!type": "?", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/undefined", |
|||
"!doc": "The value undefined." |
|||
}, |
|||
"NaN": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/NaN", |
|||
"!doc": "A value representing Not-A-Number." |
|||
}, |
|||
"Object": { |
|||
"!type": "fn()", |
|||
"getPrototypeOf": { |
|||
"!type": "fn(obj: ?) -> ?", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getPrototypeOf", |
|||
"!doc": "Returns the prototype (i.e. the internal prototype) of the specified object." |
|||
}, |
|||
"create": { |
|||
"!type": "fn(proto: ?) -> !custom:Object_create", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create", |
|||
"!doc": "Creates a new object with the specified prototype object and properties." |
|||
}, |
|||
"defineProperty": { |
|||
"!type": "fn(obj: ?, prop: string, desc: ?) -> !custom:Object_defineProperty", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty", |
|||
"!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article." |
|||
}, |
|||
"defineProperties": { |
|||
"!type": "fn(obj: ?, props: ?)", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty", |
|||
"!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article." |
|||
}, |
|||
"getOwnPropertyDescriptor": { |
|||
"!type": "fn(obj: ?, prop: string) -> ?", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor", |
|||
"!doc": "Returns a property descriptor for an own property (that is, one directly present on an object, not present by dint of being along an object's prototype chain) of a given object." |
|||
}, |
|||
"keys": { |
|||
"!type": "fn(obj: ?) -> [string]", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys", |
|||
"!doc": "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)." |
|||
}, |
|||
"getOwnPropertyNames": { |
|||
"!type": "fn(obj: ?) -> [string]", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames", |
|||
"!doc": "Returns an array of all properties (enumerable or not) found directly upon a given object." |
|||
}, |
|||
"seal": { |
|||
"!type": "fn(obj: ?)", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/seal", |
|||
"!doc": "Seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable." |
|||
}, |
|||
"isSealed": { |
|||
"!type": "fn(obj: ?) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isSealed", |
|||
"!doc": "Determine if an object is sealed." |
|||
}, |
|||
"freeze": { |
|||
"!type": "fn(obj: ?)", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/freeze", |
|||
"!doc": "Freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen." |
|||
}, |
|||
"isFrozen": { |
|||
"!type": "fn(obj: ?) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isFrozen", |
|||
"!doc": "Determine if an object is frozen." |
|||
}, |
|||
"preventExtensions": { |
|||
"!type": "fn(obj: ?)", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions", |
|||
"!doc": "Prevents new properties from ever being added to an object." |
|||
}, |
|||
"isExtensible": { |
|||
"!type": "fn(obj: ?) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible", |
|||
"!doc": "The Object.isExtensible() method determines if an object is extensible (whether it can have new properties added to it)." |
|||
}, |
|||
"prototype": { |
|||
"!stdProto": "Object", |
|||
"toString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString", |
|||
"!doc": "Returns a string representing the object." |
|||
}, |
|||
"toLocaleString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toLocaleString", |
|||
"!doc": "Returns a string representing the object. This method is meant to be overriden by derived objects for locale-specific purposes." |
|||
}, |
|||
"valueOf": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/valueOf", |
|||
"!doc": "Returns the primitive value of the specified object" |
|||
}, |
|||
"hasOwnProperty": { |
|||
"!type": "fn(prop: string) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty", |
|||
"!doc": "Returns a boolean indicating whether the object has the specified property." |
|||
}, |
|||
"propertyIsEnumerable": { |
|||
"!type": "fn(prop: string) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable", |
|||
"!doc": "Returns a Boolean indicating whether the specified property is enumerable." |
|||
}, |
|||
"isPrototypeOf": { |
|||
"!type": "fn(obj: ?) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf", |
|||
"!doc": "Tests for an object in another object's prototype chain." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object", |
|||
"!doc": "Creates an object wrapper." |
|||
}, |
|||
"Function": { |
|||
"!type": "fn(body: string) -> fn()", |
|||
"prototype": { |
|||
"!stdProto": "Function", |
|||
"apply": { |
|||
"!type": "fn(this: ?, args: [?])", |
|||
"!effects": [ |
|||
"call and return !this this=!0 !1.<i> !1.<i> !1.<i>" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply", |
|||
"!doc": "Calls a function with a given this value and arguments provided as an array (or an array like object)." |
|||
}, |
|||
"call": { |
|||
"!type": "fn(this: ?, args?: ?) -> !this.!ret", |
|||
"!effects": [ |
|||
"call and return !this this=!0 !1 !2 !3 !4" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call", |
|||
"!doc": "Calls a function with a given this value and arguments provided individually." |
|||
}, |
|||
"bind": { |
|||
"!type": "fn(this: ?, args?: ?) -> !custom:Function_bind", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind", |
|||
"!doc": "Creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function was called." |
|||
}, |
|||
"prototype": "?" |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function", |
|||
"!doc": "Every function in JavaScript is actually a Function object." |
|||
}, |
|||
"Array": { |
|||
"!type": "fn(size: number) -> !custom:Array_ctor", |
|||
"isArray": { |
|||
"!type": "fn(value: ?) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray", |
|||
"!doc": "Returns true if an object is an array, false if it is not." |
|||
}, |
|||
"prototype": { |
|||
"!stdProto": "Array", |
|||
"length": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/length", |
|||
"!doc": "An unsigned, 32-bit integer that specifies the number of elements in an array." |
|||
}, |
|||
"concat": { |
|||
"!type": "fn(other: [?]) -> !this", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/concat", |
|||
"!doc": "Returns a new array comprised of this array joined with other array(s) and/or value(s)." |
|||
}, |
|||
"join": { |
|||
"!type": "fn(separator?: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join", |
|||
"!doc": "Joins all elements of an array into a string." |
|||
}, |
|||
"splice": { |
|||
"!type": "fn(pos: number, amount: number)", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice", |
|||
"!doc": "Changes the content of an array, adding new elements while removing old elements." |
|||
}, |
|||
"pop": { |
|||
"!type": "fn() -> !this.<i>", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/pop", |
|||
"!doc": "Removes the last element from an array and returns that element." |
|||
}, |
|||
"push": { |
|||
"!type": "fn(newelt: ?) -> number", |
|||
"!effects": [ |
|||
"propagate !0 !this.<i>" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push", |
|||
"!doc": "Mutates an array by appending the given elements and returning the new length of the array." |
|||
}, |
|||
"shift": { |
|||
"!type": "fn() -> !this.<i>", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift", |
|||
"!doc": "Removes the first element from an array and returns that element. This method changes the length of the array." |
|||
}, |
|||
"unshift": { |
|||
"!type": "fn(newelt: ?) -> number", |
|||
"!effects": [ |
|||
"propagate !0 !this.<i>" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/unshift", |
|||
"!doc": "Adds one or more elements to the beginning of an array and returns the new length of the array." |
|||
}, |
|||
"slice": { |
|||
"!type": "fn(from: number, to?: number) -> !this", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice", |
|||
"!doc": "Returns a shallow copy of a portion of an array." |
|||
}, |
|||
"reverse": { |
|||
"!type": "fn()", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/reverse", |
|||
"!doc": "Reverses an array in place. The first array element becomes the last and the last becomes the first." |
|||
}, |
|||
"sort": { |
|||
"!type": "fn(compare?: fn(a: ?, b: ?) -> number)", |
|||
"!effects": [ |
|||
"call !0 !this.<i> !this.<i>" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort", |
|||
"!doc": "Sorts the elements of an array in place and returns the array." |
|||
}, |
|||
"indexOf": { |
|||
"!type": "fn(elt: ?, from?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf", |
|||
"!doc": "Returns the first index at which a given element can be found in the array, or -1 if it is not present." |
|||
}, |
|||
"lastIndexOf": { |
|||
"!type": "fn(elt: ?, from?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/lastIndexOf", |
|||
"!doc": "Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex." |
|||
}, |
|||
"every": { |
|||
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool", |
|||
"!effects": [ |
|||
"call !0 this=!1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every", |
|||
"!doc": "Tests whether all elements in the array pass the test implemented by the provided function." |
|||
}, |
|||
"some": { |
|||
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool", |
|||
"!effects": [ |
|||
"call !0 this=!1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some", |
|||
"!doc": "Tests whether some element in the array passes the test implemented by the provided function." |
|||
}, |
|||
"filter": { |
|||
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> !this", |
|||
"!effects": [ |
|||
"call !0 this=!1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter", |
|||
"!doc": "Creates a new array with all elements that pass the test implemented by the provided function." |
|||
}, |
|||
"forEach": { |
|||
"!type": "fn(f: fn(elt: ?, i: number), context?: ?)", |
|||
"!effects": [ |
|||
"call !0 this=!1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach", |
|||
"!doc": "Executes a provided function once per array element." |
|||
}, |
|||
"map": { |
|||
"!type": "fn(f: fn(elt: ?, i: number) -> ?, context?: ?) -> [!0.!ret]", |
|||
"!effects": [ |
|||
"call !0 this=!1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map", |
|||
"!doc": "Creates a new array with the results of calling a provided function on every element in this array." |
|||
}, |
|||
"reduce": { |
|||
"!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret", |
|||
"!effects": [ |
|||
"call !0 !1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/Reduce", |
|||
"!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value." |
|||
}, |
|||
"reduceRight": { |
|||
"!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret", |
|||
"!effects": [ |
|||
"call !0 !1 !this.<i> number" |
|||
], |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/ReduceRight", |
|||
"!doc": "Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array", |
|||
"!doc": "The JavaScript Array global object is a constructor for arrays, which are high-level, list-like objects." |
|||
}, |
|||
"String": { |
|||
"!type": "fn(value: ?) -> string", |
|||
"fromCharCode": { |
|||
"!type": "fn(code: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode", |
|||
"!doc": "Returns a string created by using the specified sequence of Unicode values." |
|||
}, |
|||
"prototype": { |
|||
"!stdProto": "String", |
|||
"length": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String/length", |
|||
"!doc": "Represents the length of a string." |
|||
}, |
|||
"<i>": "string", |
|||
"charAt": { |
|||
"!type": "fn(i: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charAt", |
|||
"!doc": "Returns the specified character from a string." |
|||
}, |
|||
"charCodeAt": { |
|||
"!type": "fn(i: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charCodeAt", |
|||
"!doc": "Returns the numeric Unicode value of the character at the given index (except for unicode codepoints > 0x10000)." |
|||
}, |
|||
"indexOf": { |
|||
"!type": "fn(char: string, from?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/indexOf", |
|||
"!doc": "Returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex,\nreturns -1 if the value is not found." |
|||
}, |
|||
"lastIndexOf": { |
|||
"!type": "fn(char: string, from?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf", |
|||
"!doc": "Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found. The calling string is searched backward, starting at fromIndex." |
|||
}, |
|||
"substring": { |
|||
"!type": "fn(from: number, to?: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substring", |
|||
"!doc": "Returns a subset of a string between one index and another, or through the end of the string." |
|||
}, |
|||
"substr": { |
|||
"!type": "fn(from: number, length?: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substr", |
|||
"!doc": "Returns the characters in a string beginning at the specified location through the specified number of characters." |
|||
}, |
|||
"slice": { |
|||
"!type": "fn(from: number, to?: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice", |
|||
"!doc": "Extracts a section of a string and returns a new string." |
|||
}, |
|||
"trim": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim", |
|||
"!doc": "Removes whitespace from both ends of the string." |
|||
}, |
|||
"toUpperCase": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase", |
|||
"!doc": "Returns the calling string value converted to uppercase." |
|||
}, |
|||
"toLowerCase": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLowerCase", |
|||
"!doc": "Returns the calling string value converted to lowercase." |
|||
}, |
|||
"toLocaleUpperCase": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase", |
|||
"!doc": "Returns the calling string value converted to upper case, according to any locale-specific case mappings." |
|||
}, |
|||
"toLocaleLowerCase": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase", |
|||
"!doc": "Returns the calling string value converted to lower case, according to any locale-specific case mappings." |
|||
}, |
|||
"split": { |
|||
"!type": "fn(pattern: string) -> [string]", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/split", |
|||
"!doc": "Splits a String object into an array of strings by separating the string into substrings." |
|||
}, |
|||
"concat": { |
|||
"!type": "fn(other: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/concat", |
|||
"!doc": "Combines the text of two or more strings and returns a new string." |
|||
}, |
|||
"localeCompare": { |
|||
"!type": "fn(other: string) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/localeCompare", |
|||
"!doc": "Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order." |
|||
}, |
|||
"match": { |
|||
"!type": "fn(pattern: +RegExp) -> [string]", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match", |
|||
"!doc": "Used to retrieve the matches when matching a string against a regular expression." |
|||
}, |
|||
"replace": { |
|||
"!type": "fn(pattern: +RegExp, replacement: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace", |
|||
"!doc": "Returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match." |
|||
}, |
|||
"search": { |
|||
"!type": "fn(pattern: +RegExp) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/search", |
|||
"!doc": "Executes the search for a match between a regular expression and this String object." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String", |
|||
"!doc": "The String global object is a constructor for strings, or a sequence of characters." |
|||
}, |
|||
"Number": { |
|||
"!type": "fn(value: ?) -> number", |
|||
"MAX_VALUE": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MAX_VALUE", |
|||
"!doc": "The maximum numeric value representable in JavaScript." |
|||
}, |
|||
"MIN_VALUE": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MIN_VALUE", |
|||
"!doc": "The smallest positive numeric value representable in JavaScript." |
|||
}, |
|||
"POSITIVE_INFINITY": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY", |
|||
"!doc": "A value representing the positive Infinity value." |
|||
}, |
|||
"NEGATIVE_INFINITY": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY", |
|||
"!doc": "A value representing the negative Infinity value." |
|||
}, |
|||
"prototype": { |
|||
"!stdProto": "Number", |
|||
"toString": { |
|||
"!type": "fn(radix?: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toString", |
|||
"!doc": "Returns a string representing the specified Number object" |
|||
}, |
|||
"toFixed": { |
|||
"!type": "fn(digits: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed", |
|||
"!doc": "Formats a number using fixed-point notation" |
|||
}, |
|||
"toExponential": { |
|||
"!type": "fn(digits: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toExponential", |
|||
"!doc": "Returns a string representing the Number object in exponential notation" |
|||
}, |
|||
"toPrecision": { |
|||
"!type": "fn(digits: number) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toPrecision", |
|||
"!doc": "The toPrecision() method returns a string representing the number to the specified precision." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number", |
|||
"!doc": "The Number JavaScript object is a wrapper object allowing you to work with numerical values. A Number object is created using the Number() constructor." |
|||
}, |
|||
"Boolean": { |
|||
"!type": "fn(value: ?) -> bool", |
|||
"prototype": { |
|||
"!stdProto": "Boolean" |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean", |
|||
"!doc": "The Boolean object is an object wrapper for a boolean value." |
|||
}, |
|||
"RegExp": { |
|||
"!type": "fn(source: string, flags?: string)", |
|||
"prototype": { |
|||
"!stdProto": "RegExp", |
|||
"exec": { |
|||
"!type": "fn(input: string) -> [string]", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec", |
|||
"!doc": "Executes a search for a match in a specified string. Returns a result array, or null." |
|||
}, |
|||
"test": { |
|||
"!type": "fn(input: string) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/test", |
|||
"!doc": "Executes the search for a match between a regular expression and a specified string. Returns true or false." |
|||
}, |
|||
"global": { |
|||
"!type": "bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", |
|||
"!doc": "Creates a regular expression object for matching text with a pattern." |
|||
}, |
|||
"ignoreCase": { |
|||
"!type": "bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", |
|||
"!doc": "Creates a regular expression object for matching text with a pattern." |
|||
}, |
|||
"multiline": { |
|||
"!type": "bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/multiline", |
|||
"!doc": "Reflects whether or not to search in strings across multiple lines.\n" |
|||
}, |
|||
"source": { |
|||
"!type": "string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/source", |
|||
"!doc": "A read-only property that contains the text of the pattern, excluding the forward slashes.\n" |
|||
}, |
|||
"lastIndex": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/lastIndex", |
|||
"!doc": "A read/write integer property that specifies the index at which to start the next match." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", |
|||
"!doc": "Creates a regular expression object for matching text with a pattern." |
|||
}, |
|||
"Date": { |
|||
"!type": "fn(ms: number)", |
|||
"parse": { |
|||
"!type": "fn(source: string) -> +Date", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse", |
|||
"!doc": "Parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC." |
|||
}, |
|||
"UTC": { |
|||
"!type": "fn(year: number, month: number, date: number, hour?: number, min?: number, sec?: number, ms?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/UTC", |
|||
"!doc": "Accepts the same parameters as the longest form of the constructor, and returns the number of milliseconds in a Date object since January 1, 1970, 00:00:00, universal time." |
|||
}, |
|||
"now": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now", |
|||
"!doc": "Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC." |
|||
}, |
|||
"prototype": { |
|||
"toUTCString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toUTCString", |
|||
"!doc": "Converts a date to a string, using the universal time convention." |
|||
}, |
|||
"toISOString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString", |
|||
"!doc": "JavaScript provides a direct way to convert a date object into a string in ISO format, the ISO 8601 Extended Format." |
|||
}, |
|||
"toDateString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toDateString", |
|||
"!doc": "Returns the date portion of a Date object in human readable form in American English." |
|||
}, |
|||
"toTimeString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toTimeString", |
|||
"!doc": "Returns the time portion of a Date object in human readable form in American English." |
|||
}, |
|||
"toLocaleDateString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleDateString", |
|||
"!doc": "Converts a date to a string, returning the \"date\" portion using the operating system's locale's conventions.\n" |
|||
}, |
|||
"toLocaleTimeString": { |
|||
"!type": "fn() -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString", |
|||
"!doc": "Converts a date to a string, returning the \"time\" portion using the current locale's conventions." |
|||
}, |
|||
"getTime": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTime", |
|||
"!doc": "Returns the numeric value corresponding to the time for the specified date according to universal time." |
|||
}, |
|||
"getFullYear": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getFullYear", |
|||
"!doc": "Returns the year of the specified date according to local time." |
|||
}, |
|||
"getYear": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getYear", |
|||
"!doc": "Returns the year in the specified date according to local time." |
|||
}, |
|||
"getMonth": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMonth", |
|||
"!doc": "Returns the month in the specified date according to local time." |
|||
}, |
|||
"getUTCMonth": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMonth", |
|||
"!doc": "Returns the month of the specified date according to universal time.\n" |
|||
}, |
|||
"getDate": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDate", |
|||
"!doc": "Returns the day of the month for the specified date according to local time." |
|||
}, |
|||
"getUTCDate": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDate", |
|||
"!doc": "Returns the day (date) of the month in the specified date according to universal time.\n" |
|||
}, |
|||
"getDay": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDay", |
|||
"!doc": "Returns the day of the week for the specified date according to local time." |
|||
}, |
|||
"getUTCDay": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDay", |
|||
"!doc": "Returns the day of the week in the specified date according to universal time.\n" |
|||
}, |
|||
"getHours": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getHours", |
|||
"!doc": "Returns the hour for the specified date according to local time." |
|||
}, |
|||
"getUTCHours": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCHours", |
|||
"!doc": "Returns the hours in the specified date according to universal time.\n" |
|||
}, |
|||
"getMinutes": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMinutes", |
|||
"!doc": "Returns the minutes in the specified date according to local time." |
|||
}, |
|||
"getUTCMinutes": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date", |
|||
"!doc": "Creates JavaScript Date instances which let you work with dates and times." |
|||
}, |
|||
"getSeconds": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getSeconds", |
|||
"!doc": "Returns the seconds in the specified date according to local time." |
|||
}, |
|||
"getUTCSeconds": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCSeconds", |
|||
"!doc": "Returns the seconds in the specified date according to universal time.\n" |
|||
}, |
|||
"getMilliseconds": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMilliseconds", |
|||
"!doc": "Returns the milliseconds in the specified date according to local time." |
|||
}, |
|||
"getUTCMilliseconds": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds", |
|||
"!doc": "Returns the milliseconds in the specified date according to universal time.\n" |
|||
}, |
|||
"getTimezoneOffset": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset", |
|||
"!doc": "Returns the time-zone offset from UTC, in minutes, for the current locale." |
|||
}, |
|||
"setTime": { |
|||
"!type": "fn(date: +Date) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setTime", |
|||
"!doc": "Sets the Date object to the time represented by a number of milliseconds since January 1, 1970, 00:00:00 UTC.\n" |
|||
}, |
|||
"setFullYear": { |
|||
"!type": "fn(year: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setFullYear", |
|||
"!doc": "Sets the full year for a specified date according to local time.\n" |
|||
}, |
|||
"setUTCFullYear": { |
|||
"!type": "fn(year: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCFullYear", |
|||
"!doc": "Sets the full year for a specified date according to universal time.\n" |
|||
}, |
|||
"setMonth": { |
|||
"!type": "fn(month: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMonth", |
|||
"!doc": "Set the month for a specified date according to local time." |
|||
}, |
|||
"setUTCMonth": { |
|||
"!type": "fn(month: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMonth", |
|||
"!doc": "Sets the month for a specified date according to universal time.\n" |
|||
}, |
|||
"setDate": { |
|||
"!type": "fn(day: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setDate", |
|||
"!doc": "Sets the day of the month for a specified date according to local time." |
|||
}, |
|||
"setUTCDate": { |
|||
"!type": "fn(day: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCDate", |
|||
"!doc": "Sets the day of the month for a specified date according to universal time.\n" |
|||
}, |
|||
"setHours": { |
|||
"!type": "fn(hour: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setHours", |
|||
"!doc": "Sets the hours for a specified date according to local time, and returns the number of milliseconds since 1 January 1970 00:00:00 UTC until the time represented by the updated Date instance." |
|||
}, |
|||
"setUTCHours": { |
|||
"!type": "fn(hour: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCHours", |
|||
"!doc": "Sets the hour for a specified date according to universal time.\n" |
|||
}, |
|||
"setMinutes": { |
|||
"!type": "fn(min: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMinutes", |
|||
"!doc": "Sets the minutes for a specified date according to local time." |
|||
}, |
|||
"setUTCMinutes": { |
|||
"!type": "fn(min: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMinutes", |
|||
"!doc": "Sets the minutes for a specified date according to universal time.\n" |
|||
}, |
|||
"setSeconds": { |
|||
"!type": "fn(sec: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setSeconds", |
|||
"!doc": "Sets the seconds for a specified date according to local time." |
|||
}, |
|||
"setUTCSeconds": { |
|||
"!type": "fn(sec: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCSeconds", |
|||
"!doc": "Sets the seconds for a specified date according to universal time.\n" |
|||
}, |
|||
"setMilliseconds": { |
|||
"!type": "fn(ms: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMilliseconds", |
|||
"!doc": "Sets the milliseconds for a specified date according to local time.\n" |
|||
}, |
|||
"setUTCMilliseconds": { |
|||
"!type": "fn(ms: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds", |
|||
"!doc": "Sets the milliseconds for a specified date according to universal time.\n" |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date", |
|||
"!doc": "Creates JavaScript Date instances which let you work with dates and times." |
|||
}, |
|||
"Error": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": { |
|||
"name": { |
|||
"!type": "string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/name", |
|||
"!doc": "A name for the type of error." |
|||
}, |
|||
"message": { |
|||
"!type": "string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/message", |
|||
"!doc": "A human-readable description of the error." |
|||
} |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error", |
|||
"!doc": "Creates an error object." |
|||
}, |
|||
"SyntaxError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/SyntaxError", |
|||
"!doc": "Represents an error when trying to interpret syntactically invalid code." |
|||
}, |
|||
"ReferenceError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError", |
|||
"!doc": "Represents an error when a non-existent variable is referenced." |
|||
}, |
|||
"URIError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/URIError", |
|||
"!doc": "Represents an error when a malformed URI is encountered." |
|||
}, |
|||
"EvalError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/EvalError", |
|||
"!doc": "Represents an error regarding the eval function." |
|||
}, |
|||
"RangeError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RangeError", |
|||
"!doc": "Represents an error when a number is not within the correct range allowed." |
|||
}, |
|||
"TypeError": { |
|||
"!type": "fn(message: string)", |
|||
"prototype": "Error.prototype", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/TypeError", |
|||
"!doc": "Represents an error an error when a value is not of the expected type." |
|||
}, |
|||
"parseInt": { |
|||
"!type": "fn(string: string, radix?: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt", |
|||
"!doc": "Parses a string argument and returns an integer of the specified radix or base." |
|||
}, |
|||
"parseFloat": { |
|||
"!type": "fn(string: string) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseFloat", |
|||
"!doc": "Parses a string argument and returns a floating point number." |
|||
}, |
|||
"isNaN": { |
|||
"!type": "fn(value: number) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isNaN", |
|||
"!doc": "Determines whether a value is NaN or not. Be careful, this function is broken. You may be interested in ECMAScript 6 Number.isNaN." |
|||
}, |
|||
"isFinite": { |
|||
"!type": "fn(value: number) -> bool", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isFinite", |
|||
"!doc": "Determines whether the passed value is a finite number." |
|||
}, |
|||
"eval": { |
|||
"!type": "fn(code: string) -> ?", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval", |
|||
"!doc": "Evaluates JavaScript code represented as a string." |
|||
}, |
|||
"encodeURI": { |
|||
"!type": "fn(uri: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI", |
|||
"!doc": "Encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)." |
|||
}, |
|||
"encodeURIComponent": { |
|||
"!type": "fn(uri: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent", |
|||
"!doc": "Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)." |
|||
}, |
|||
"decodeURI": { |
|||
"!type": "fn(uri: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURI", |
|||
"!doc": "Decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine." |
|||
}, |
|||
"decodeURIComponent": { |
|||
"!type": "fn(uri: string) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURIComponent", |
|||
"!doc": "Decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine." |
|||
}, |
|||
"Math": { |
|||
"E": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/E", |
|||
"!doc": "The base of natural logarithms, e, approximately 2.718." |
|||
}, |
|||
"LN2": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN2", |
|||
"!doc": "The natural logarithm of 2, approximately 0.693." |
|||
}, |
|||
"LN10": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN10", |
|||
"!doc": "The natural logarithm of 10, approximately 2.302." |
|||
}, |
|||
"LOG2E": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG2E", |
|||
"!doc": "The base 2 logarithm of E (approximately 1.442)." |
|||
}, |
|||
"LOG10E": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG10E", |
|||
"!doc": "The base 10 logarithm of E (approximately 0.434)." |
|||
}, |
|||
"SQRT1_2": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT1_2", |
|||
"!doc": "The square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707." |
|||
}, |
|||
"SQRT2": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT2", |
|||
"!doc": "The square root of 2, approximately 1.414." |
|||
}, |
|||
"PI": { |
|||
"!type": "number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/PI", |
|||
"!doc": "The ratio of the circumference of a circle to its diameter, approximately 3.14159." |
|||
}, |
|||
"abs": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/abs", |
|||
"!doc": "Returns the absolute value of a number." |
|||
}, |
|||
"cos": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/cos", |
|||
"!doc": "Returns the cosine of a number." |
|||
}, |
|||
"sin": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sin", |
|||
"!doc": "Returns the sine of a number." |
|||
}, |
|||
"tan": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/tan", |
|||
"!doc": "Returns the tangent of a number." |
|||
}, |
|||
"acos": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/acos", |
|||
"!doc": "Returns the arccosine (in radians) of a number." |
|||
}, |
|||
"asin": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/asin", |
|||
"!doc": "Returns the arcsine (in radians) of a number." |
|||
}, |
|||
"atan": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan", |
|||
"!doc": "Returns the arctangent (in radians) of a number." |
|||
}, |
|||
"atan2": { |
|||
"!type": "fn(y: number, x: number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan2", |
|||
"!doc": "Returns the arctangent of the quotient of its arguments." |
|||
}, |
|||
"ceil": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/ceil", |
|||
"!doc": "Returns the smallest integer greater than or equal to a number." |
|||
}, |
|||
"floor": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/floor", |
|||
"!doc": "Returns the largest integer less than or equal to a number." |
|||
}, |
|||
"round": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/round", |
|||
"!doc": "Returns the value of a number rounded to the nearest integer." |
|||
}, |
|||
"exp": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/exp", |
|||
"!doc": "Returns Ex, where x is the argument, and E is Euler's constant, the base of the natural logarithms." |
|||
}, |
|||
"log": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/log", |
|||
"!doc": "Returns the natural logarithm (base E) of a number." |
|||
}, |
|||
"sqrt": { |
|||
"!type": "fn(number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sqrt", |
|||
"!doc": "Returns the square root of a number." |
|||
}, |
|||
"pow": { |
|||
"!type": "fn(number, number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow", |
|||
"!doc": "Returns base to the exponent power, that is, baseexponent." |
|||
}, |
|||
"max": { |
|||
"!type": "fn(number, number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/max", |
|||
"!doc": "Returns the largest of zero or more numbers." |
|||
}, |
|||
"min": { |
|||
"!type": "fn(number, number) -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/min", |
|||
"!doc": "Returns the smallest of zero or more numbers." |
|||
}, |
|||
"random": { |
|||
"!type": "fn() -> number", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random", |
|||
"!doc": "Returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range." |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math", |
|||
"!doc": "A built-in object that has properties and methods for mathematical constants and functions." |
|||
}, |
|||
"JSON": { |
|||
"parse": { |
|||
"!type": "fn(json: string) -> ?", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse", |
|||
"!doc": "Parse a string as JSON, optionally transforming the value produced by parsing." |
|||
}, |
|||
"stringify": { |
|||
"!type": "fn(value: ?) -> string", |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify", |
|||
"!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified." |
|||
}, |
|||
"!url": "https://developer.mozilla.org/en-US/docs/JSON", |
|||
"!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference." |
|||
} |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,26 @@ |
|||
(function(root, mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
return mod(exports); |
|||
if (typeof define == "function" && define.amd) // AMD
|
|||
return define(["exports"], mod); |
|||
mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env
|
|||
})(this, function(exports) { |
|||
function on(type, f) { |
|||
var handlers = this._handlers || (this._handlers = Object.create(null)); |
|||
(handlers[type] || (handlers[type] = [])).push(f); |
|||
} |
|||
function off(type, f) { |
|||
var arr = this._handlers && this._handlers[type]; |
|||
if (arr) for (var i = 0; i < arr.length; ++i) |
|||
if (arr[i] == f) { arr.splice(i, 1); break; } |
|||
} |
|||
function signal(type, a1, a2, a3, a4) { |
|||
var arr = this._handlers && this._handlers[type]; |
|||
if (arr) for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4); |
|||
} |
|||
|
|||
exports.mixin = function(obj) { |
|||
obj.on = on; obj.off = off; obj.signal = signal; |
|||
return obj; |
|||
}; |
|||
}); |
@ -0,0 +1,697 @@ |
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
|||
|
|||
// Glue code between CodeMirror and Tern.
|
|||
//
|
|||
// Create a CodeMirror.TernServer to wrap an actual Tern server,
|
|||
// register open documents (CodeMirror.Doc instances) with it, and
|
|||
// call its methods to activate the assisting functions that Tern
|
|||
// provides.
|
|||
//
|
|||
// Options supported (all optional):
|
|||
// * defs: An array of JSON definition data structures.
|
|||
// * plugins: An object mapping plugin names to configuration
|
|||
// options.
|
|||
// * getFile: A function(name, c) that can be used to access files in
|
|||
// the project that haven't been loaded yet. Simply do c(null) to
|
|||
// indicate that a file is not available.
|
|||
// * fileFilter: A function(value, docName, doc) that will be applied
|
|||
// to documents before passing them on to Tern.
|
|||
// * switchToDoc: A function(name, doc) that should, when providing a
|
|||
// multi-file view, switch the view or focus to the named file.
|
|||
// * showError: A function(editor, message) that can be used to
|
|||
// override the way errors are displayed.
|
|||
// * completionTip: Customize the content in tooltips for completions.
|
|||
// Is passed a single argument—the completion's data as returned by
|
|||
// Tern—and may return a string, DOM node, or null to indicate that
|
|||
// no tip should be shown. By default the docstring is shown.
|
|||
// * typeTip: Like completionTip, but for the tooltips shown for type
|
|||
// queries.
|
|||
// * responseFilter: A function(doc, query, request, error, data) that
|
|||
// will be applied to the Tern responses before treating them
|
|||
//
|
|||
//
|
|||
// It is possible to run the Tern server in a web worker by specifying
|
|||
// these additional options:
|
|||
// * useWorker: Set to true to enable web worker mode. You'll probably
|
|||
// want to feature detect the actual value you use here, for example
|
|||
// !!window.Worker.
|
|||
// * workerScript: The main script of the worker. Point this to
|
|||
// wherever you are hosting worker.js from this directory.
|
|||
// * workerDeps: An array of paths pointing (relative to workerScript)
|
|||
// to the Acorn and Tern libraries and any Tern plugins you want to
|
|||
// load. Or, if you minified those into a single script and included
|
|||
// them in the workerScript, simply leave this undefined.
|
|||
|
|||
(function(mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
mod(require("../../lib/codemirror")); |
|||
else if (typeof define == "function" && define.amd) // AMD
|
|||
define(["../../lib/codemirror"], mod); |
|||
else // Plain browser env
|
|||
mod(CodeMirror); |
|||
})(function(CodeMirror) { |
|||
"use strict"; |
|||
// declare global: tern
|
|||
|
|||
CodeMirror.TernServer = function(options) { |
|||
var self = this; |
|||
this.options = options || {}; |
|||
var plugins = this.options.plugins || (this.options.plugins = {}); |
|||
if (!plugins.doc_comment) plugins.doc_comment = true; |
|||
if (this.options.useWorker) { |
|||
this.server = new WorkerServer(this); |
|||
} else { |
|||
this.server = new tern.Server({ |
|||
getFile: function(name, c) { return getFile(self, name, c); }, |
|||
async: true, |
|||
defs: this.options.defs || [], |
|||
plugins: plugins |
|||
}); |
|||
} |
|||
this.docs = Object.create(null); |
|||
this.trackChange = function(doc, change) { trackChange(self, doc, change); }; |
|||
|
|||
this.cachedArgHints = null; |
|||
this.activeArgHints = null; |
|||
this.jumpStack = []; |
|||
|
|||
this.getHint = function(cm, c) { return hint(self, cm, c); }; |
|||
this.getHint.async = true; |
|||
}; |
|||
|
|||
CodeMirror.TernServer.prototype = { |
|||
addDoc: function(name, doc) { |
|||
var data = {doc: doc, name: name, changed: null}; |
|||
this.server.addFile(name, docValue(this, data)); |
|||
CodeMirror.on(doc, "change", this.trackChange); |
|||
return this.docs[name] = data; |
|||
}, |
|||
|
|||
delDoc: function(id) { |
|||
var found = resolveDoc(this, id); |
|||
if (!found) return; |
|||
CodeMirror.off(found.doc, "change", this.trackChange); |
|||
delete this.docs[found.name]; |
|||
this.server.delFile(found.name); |
|||
}, |
|||
|
|||
hideDoc: function(id) { |
|||
closeArgHints(this); |
|||
var found = resolveDoc(this, id); |
|||
if (found && found.changed) sendDoc(this, found); |
|||
}, |
|||
|
|||
complete: function(cm) { |
|||
cm.showHint({hint: this.getHint}); |
|||
}, |
|||
|
|||
showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); }, |
|||
|
|||
showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); }, |
|||
|
|||
updateArgHints: function(cm) { updateArgHints(this, cm); }, |
|||
|
|||
jumpToDef: function(cm) { jumpToDef(this, cm); }, |
|||
|
|||
jumpBack: function(cm) { jumpBack(this, cm); }, |
|||
|
|||
rename: function(cm) { rename(this, cm); }, |
|||
|
|||
selectName: function(cm) { selectName(this, cm); }, |
|||
|
|||
request: function (cm, query, c, pos) { |
|||
var self = this; |
|||
var doc = findDoc(this, cm.getDoc()); |
|||
var request = buildRequest(this, doc, query, pos); |
|||
|
|||
this.server.request(request, function (error, data) { |
|||
if (!error && self.options.responseFilter) |
|||
data = self.options.responseFilter(doc, query, request, error, data); |
|||
c(error, data); |
|||
}); |
|||
}, |
|||
|
|||
destroy: function () { |
|||
if (this.worker) { |
|||
this.worker.terminate(); |
|||
this.worker = null; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
var Pos = CodeMirror.Pos; |
|||
var cls = "CodeMirror-Tern-"; |
|||
var bigDoc = 250; |
|||
|
|||
function getFile(ts, name, c) { |
|||
var buf = ts.docs[name]; |
|||
if (buf) |
|||
c(docValue(ts, buf)); |
|||
else if (ts.options.getFile) |
|||
ts.options.getFile(name, c); |
|||
else |
|||
c(null); |
|||
} |
|||
|
|||
function findDoc(ts, doc, name) { |
|||
for (var n in ts.docs) { |
|||
var cur = ts.docs[n]; |
|||
if (cur.doc == doc) return cur; |
|||
} |
|||
if (!name) for (var i = 0;; ++i) { |
|||
n = "[doc" + (i || "") + "]"; |
|||
if (!ts.docs[n]) { name = n; break; } |
|||
} |
|||
return ts.addDoc(name, doc); |
|||
} |
|||
|
|||
function resolveDoc(ts, id) { |
|||
if (typeof id == "string") return ts.docs[id]; |
|||
if (id instanceof CodeMirror) id = id.getDoc(); |
|||
if (id instanceof CodeMirror.Doc) return findDoc(ts, id); |
|||
} |
|||
|
|||
function trackChange(ts, doc, change) { |
|||
var data = findDoc(ts, doc); |
|||
|
|||
var argHints = ts.cachedArgHints; |
|||
if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0) |
|||
ts.cachedArgHints = null; |
|||
|
|||
var changed = data.changed; |
|||
if (changed == null) |
|||
data.changed = changed = {from: change.from.line, to: change.from.line}; |
|||
var end = change.from.line + (change.text.length - 1); |
|||
if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end); |
|||
if (end >= changed.to) changed.to = end + 1; |
|||
if (changed.from > change.from.line) changed.from = change.from.line; |
|||
|
|||
if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() { |
|||
if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data); |
|||
}, 200); |
|||
} |
|||
|
|||
function sendDoc(ts, doc) { |
|||
ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) { |
|||
if (error) window.console.error(error); |
|||
else doc.changed = null; |
|||
}); |
|||
} |
|||
|
|||
// Completion
|
|||
|
|||
function hint(ts, cm, c) { |
|||
ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) { |
|||
if (error) return showError(ts, cm, error); |
|||
var completions = [], after = ""; |
|||
var from = data.start, to = data.end; |
|||
if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" && |
|||
cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]") |
|||
after = "\"]"; |
|||
|
|||
for (var i = 0; i < data.completions.length; ++i) { |
|||
var completion = data.completions[i], className = typeToIcon(completion.type); |
|||
if (data.guess) className += " " + cls + "guess"; |
|||
completions.push({text: completion.name + after, |
|||
displayText: completion.name, |
|||
className: className, |
|||
data: completion}); |
|||
} |
|||
|
|||
var obj = {from: from, to: to, list: completions}; |
|||
var tooltip = null; |
|||
CodeMirror.on(obj, "close", function() { remove(tooltip); }); |
|||
CodeMirror.on(obj, "update", function() { remove(tooltip); }); |
|||
CodeMirror.on(obj, "select", function(cur, node) { |
|||
remove(tooltip); |
|||
var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc; |
|||
if (content) { |
|||
tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, |
|||
node.getBoundingClientRect().top + window.pageYOffset, content); |
|||
tooltip.className += " " + cls + "hint-doc"; |
|||
} |
|||
}); |
|||
c(obj); |
|||
}); |
|||
} |
|||
|
|||
function typeToIcon(type) { |
|||
var suffix; |
|||
if (type == "?") suffix = "unknown"; |
|||
else if (type == "number" || type == "string" || type == "bool") suffix = type; |
|||
else if (/^fn\(/.test(type)) suffix = "fn"; |
|||
else if (/^\[/.test(type)) suffix = "array"; |
|||
else suffix = "object"; |
|||
return cls + "completion " + cls + "completion-" + suffix; |
|||
} |
|||
|
|||
// Type queries
|
|||
|
|||
function showContextInfo(ts, cm, pos, queryName, c) { |
|||
ts.request(cm, queryName, function(error, data) { |
|||
if (error) return showError(ts, cm, error); |
|||
if (ts.options.typeTip) { |
|||
var tip = ts.options.typeTip(data); |
|||
} else { |
|||
var tip = elt("span", null, elt("strong", null, data.type || "not found")); |
|||
if (data.doc) |
|||
tip.appendChild(document.createTextNode(" — " + data.doc)); |
|||
if (data.url) { |
|||
tip.appendChild(document.createTextNode(" ")); |
|||
var child = tip.appendChild(elt("a", null, "[docs]")); |
|||
child.href = data.url; |
|||
child.target = "_blank"; |
|||
} |
|||
} |
|||
tempTooltip(cm, tip); |
|||
if (c) c(); |
|||
}, pos); |
|||
} |
|||
|
|||
// Maintaining argument hints
|
|||
|
|||
function updateArgHints(ts, cm) { |
|||
closeArgHints(ts); |
|||
|
|||
if (cm.somethingSelected()) return; |
|||
var state = cm.getTokenAt(cm.getCursor()).state; |
|||
var inner = CodeMirror.innerMode(cm.getMode(), state); |
|||
if (inner.mode.name != "javascript") return; |
|||
var lex = inner.state.lexical; |
|||
if (lex.info != "call") return; |
|||
|
|||
var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize"); |
|||
for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) { |
|||
var str = cm.getLine(line), extra = 0; |
|||
for (var pos = 0;;) { |
|||
var tab = str.indexOf("\t", pos); |
|||
if (tab == -1) break; |
|||
extra += tabSize - (tab + extra) % tabSize - 1; |
|||
pos = tab + 1; |
|||
} |
|||
ch = lex.column - extra; |
|||
if (str.charAt(ch) == "(") {found = true; break;} |
|||
} |
|||
if (!found) return; |
|||
|
|||
var start = Pos(line, ch); |
|||
var cache = ts.cachedArgHints; |
|||
if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) |
|||
return showArgHints(ts, cm, argPos); |
|||
|
|||
ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { |
|||
if (error || !data.type || !(/^fn\(/).test(data.type)) return; |
|||
ts.cachedArgHints = { |
|||
start: pos, |
|||
type: parseFnType(data.type), |
|||
name: data.exprName || data.name || "fn", |
|||
guess: data.guess, |
|||
doc: cm.getDoc() |
|||
}; |
|||
showArgHints(ts, cm, argPos); |
|||
}); |
|||
} |
|||
|
|||
function showArgHints(ts, cm, pos) { |
|||
closeArgHints(ts); |
|||
|
|||
var cache = ts.cachedArgHints, tp = cache.type; |
|||
var tip = elt("span", cache.guess ? cls + "fhint-guess" : null, |
|||
elt("span", cls + "fname", cache.name), "("); |
|||
for (var i = 0; i < tp.args.length; ++i) { |
|||
if (i) tip.appendChild(document.createTextNode(", ")); |
|||
var arg = tp.args[i]; |
|||
tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?")); |
|||
if (arg.type != "?") { |
|||
tip.appendChild(document.createTextNode(":\u00a0")); |
|||
tip.appendChild(elt("span", cls + "type", arg.type)); |
|||
} |
|||
} |
|||
tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")")); |
|||
if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype)); |
|||
var place = cm.cursorCoords(null, "page"); |
|||
ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip); |
|||
} |
|||
|
|||
function parseFnType(text) { |
|||
var args = [], pos = 3; |
|||
|
|||
function skipMatching(upto) { |
|||
var depth = 0, start = pos; |
|||
for (;;) { |
|||
var next = text.charAt(pos); |
|||
if (upto.test(next) && !depth) return text.slice(start, pos); |
|||
if (/[{\[\(]/.test(next)) ++depth; |
|||
else if (/[}\]\)]/.test(next)) --depth; |
|||
++pos; |
|||
} |
|||
} |
|||
|
|||
// Parse arguments
|
|||
if (text.charAt(pos) != ")") for (;;) { |
|||
var name = text.slice(pos).match(/^([^, \(\[\{]+): /); |
|||
if (name) { |
|||
pos += name[0].length; |
|||
name = name[1]; |
|||
} |
|||
args.push({name: name, type: skipMatching(/[\),]/)}); |
|||
if (text.charAt(pos) == ")") break; |
|||
pos += 2; |
|||
} |
|||
|
|||
var rettype = text.slice(pos).match(/^\) -> (.*)$/); |
|||
|
|||
return {args: args, rettype: rettype && rettype[1]}; |
|||
} |
|||
|
|||
// Moving to the definition of something
|
|||
|
|||
function jumpToDef(ts, cm) { |
|||
function inner(varName) { |
|||
var req = {type: "definition", variable: varName || null}; |
|||
var doc = findDoc(ts, cm.getDoc()); |
|||
ts.server.request(buildRequest(ts, doc, req), function(error, data) { |
|||
if (error) return showError(ts, cm, error); |
|||
if (!data.file && data.url) { window.open(data.url); return; } |
|||
|
|||
if (data.file) { |
|||
var localDoc = ts.docs[data.file], found; |
|||
if (localDoc && (found = findContext(localDoc.doc, data))) { |
|||
ts.jumpStack.push({file: doc.name, |
|||
start: cm.getCursor("from"), |
|||
end: cm.getCursor("to")}); |
|||
moveTo(ts, doc, localDoc, found.start, found.end); |
|||
return; |
|||
} |
|||
} |
|||
showError(ts, cm, "Could not find a definition."); |
|||
}); |
|||
} |
|||
|
|||
if (!atInterestingExpression(cm)) |
|||
dialog(cm, "Jump to variable", function(name) { if (name) inner(name); }); |
|||
else |
|||
inner(); |
|||
} |
|||
|
|||
function jumpBack(ts, cm) { |
|||
var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file]; |
|||
if (!doc) return; |
|||
moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end); |
|||
} |
|||
|
|||
function moveTo(ts, curDoc, doc, start, end) { |
|||
doc.doc.setSelection(start, end); |
|||
if (curDoc != doc && ts.options.switchToDoc) { |
|||
closeArgHints(ts); |
|||
ts.options.switchToDoc(doc.name, doc.doc); |
|||
} |
|||
} |
|||
|
|||
// The {line,ch} representation of positions makes this rather awkward.
|
|||
function findContext(doc, data) { |
|||
var before = data.context.slice(0, data.contextOffset).split("\n"); |
|||
var startLine = data.start.line - (before.length - 1); |
|||
var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length); |
|||
|
|||
var text = doc.getLine(startLine).slice(start.ch); |
|||
for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur) |
|||
text += "\n" + doc.getLine(cur); |
|||
if (text.slice(0, data.context.length) == data.context) return data; |
|||
|
|||
var cursor = doc.getSearchCursor(data.context, 0, false); |
|||
var nearest, nearestDist = Infinity; |
|||
while (cursor.findNext()) { |
|||
var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000; |
|||
if (!dist) dist = Math.abs(from.ch - start.ch); |
|||
if (dist < nearestDist) { nearest = from; nearestDist = dist; } |
|||
} |
|||
if (!nearest) return null; |
|||
|
|||
if (before.length == 1) |
|||
nearest.ch += before[0].length; |
|||
else |
|||
nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length); |
|||
if (data.start.line == data.end.line) |
|||
var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch)); |
|||
else |
|||
var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch); |
|||
return {start: nearest, end: end}; |
|||
} |
|||
|
|||
function atInterestingExpression(cm) { |
|||
var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos); |
|||
if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false; |
|||
return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1)); |
|||
} |
|||
|
|||
// Variable renaming
|
|||
|
|||
function rename(ts, cm) { |
|||
var token = cm.getTokenAt(cm.getCursor()); |
|||
if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable"); |
|||
dialog(cm, "New name for " + token.string, function(newName) { |
|||
ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) { |
|||
if (error) return showError(ts, cm, error); |
|||
applyChanges(ts, data.changes); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function selectName(ts, cm) { |
|||
var name = findDoc(ts, cm.doc).name; |
|||
ts.request(cm, {type: "refs"}, function(error, data) { |
|||
if (error) return showError(ts, cm, error); |
|||
var ranges = [], cur = 0; |
|||
for (var i = 0; i < data.refs.length; i++) { |
|||
var ref = data.refs[i]; |
|||
if (ref.file == name) { |
|||
ranges.push({anchor: ref.start, head: ref.end}); |
|||
if (cmpPos(cur, ref.start) >= 0 && cmpPos(cur, ref.end) <= 0) |
|||
cur = ranges.length - 1; |
|||
} |
|||
} |
|||
cm.setSelections(ranges, cur); |
|||
}); |
|||
} |
|||
|
|||
var nextChangeOrig = 0; |
|||
function applyChanges(ts, changes) { |
|||
var perFile = Object.create(null); |
|||
for (var i = 0; i < changes.length; ++i) { |
|||
var ch = changes[i]; |
|||
(perFile[ch.file] || (perFile[ch.file] = [])).push(ch); |
|||
} |
|||
for (var file in perFile) { |
|||
var known = ts.docs[file], chs = perFile[file];; |
|||
if (!known) continue; |
|||
chs.sort(function(a, b) { return cmpPos(b.start, a.start); }); |
|||
var origin = "*rename" + (++nextChangeOrig); |
|||
for (var i = 0; i < chs.length; ++i) { |
|||
var ch = chs[i]; |
|||
known.doc.replaceRange(ch.text, ch.start, ch.end, origin); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Generic request-building helper
|
|||
|
|||
function buildRequest(ts, doc, query, pos) { |
|||
var files = [], offsetLines = 0, allowFragments = !query.fullDocs; |
|||
if (!allowFragments) delete query.fullDocs; |
|||
if (typeof query == "string") query = {type: query}; |
|||
query.lineCharPositions = true; |
|||
if (query.end == null) { |
|||
query.end = pos || doc.doc.getCursor("end"); |
|||
if (doc.doc.somethingSelected()) |
|||
query.start = doc.doc.getCursor("start"); |
|||
} |
|||
var startPos = query.start || query.end; |
|||
|
|||
if (doc.changed) { |
|||
if (doc.doc.lineCount() > bigDoc && allowFragments !== false && |
|||
doc.changed.to - doc.changed.from < 100 && |
|||
doc.changed.from <= startPos.line && doc.changed.to > query.end.line) { |
|||
files.push(getFragmentAround(doc, startPos, query.end)); |
|||
query.file = "#0"; |
|||
var offsetLines = files[0].offsetLines; |
|||
if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch); |
|||
query.end = Pos(query.end.line - offsetLines, query.end.ch); |
|||
} else { |
|||
files.push({type: "full", |
|||
name: doc.name, |
|||
text: docValue(ts, doc)}); |
|||
query.file = doc.name; |
|||
doc.changed = null; |
|||
} |
|||
} else { |
|||
query.file = doc.name; |
|||
} |
|||
for (var name in ts.docs) { |
|||
var cur = ts.docs[name]; |
|||
if (cur.changed && cur != doc) { |
|||
files.push({type: "full", name: cur.name, text: docValue(ts, cur)}); |
|||
cur.changed = null; |
|||
} |
|||
} |
|||
|
|||
return {query: query, files: files}; |
|||
} |
|||
|
|||
function getFragmentAround(data, start, end) { |
|||
var doc = data.doc; |
|||
var minIndent = null, minLine = null, endLine, tabSize = 4; |
|||
for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) { |
|||
var line = doc.getLine(p), fn = line.search(/\bfunction\b/); |
|||
if (fn < 0) continue; |
|||
var indent = CodeMirror.countColumn(line, null, tabSize); |
|||
if (minIndent != null && minIndent <= indent) continue; |
|||
minIndent = indent; |
|||
minLine = p; |
|||
} |
|||
if (minLine == null) minLine = min; |
|||
var max = Math.min(doc.lastLine(), end.line + 20); |
|||
if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize)) |
|||
endLine = max; |
|||
else for (endLine = end.line + 1; endLine < max; ++endLine) { |
|||
var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize); |
|||
if (indent <= minIndent) break; |
|||
} |
|||
var from = Pos(minLine, 0); |
|||
|
|||
return {type: "part", |
|||
name: data.name, |
|||
offsetLines: from.line, |
|||
text: doc.getRange(from, Pos(endLine, 0))}; |
|||
} |
|||
|
|||
// Generic utilities
|
|||
|
|||
var cmpPos = CodeMirror.cmpPos; |
|||
|
|||
function elt(tagname, cls /*, ... elts*/) { |
|||
var e = document.createElement(tagname); |
|||
if (cls) e.className = cls; |
|||
for (var i = 2; i < arguments.length; ++i) { |
|||
var elt = arguments[i]; |
|||
if (typeof elt == "string") elt = document.createTextNode(elt); |
|||
e.appendChild(elt); |
|||
} |
|||
return e; |
|||
} |
|||
|
|||
function dialog(cm, text, f) { |
|||
if (cm.openDialog) |
|||
cm.openDialog(text + ": <input type=text>", f); |
|||
else |
|||
f(prompt(text, "")); |
|||
} |
|||
|
|||
// Tooltips
|
|||
|
|||
function tempTooltip(cm, content) { |
|||
if (cm.state.ternTooltip) remove(cm.state.ternTooltip); |
|||
var where = cm.cursorCoords(); |
|||
var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content); |
|||
function maybeClear() { |
|||
old = true; |
|||
if (!mouseOnTip) clear(); |
|||
} |
|||
function clear() { |
|||
cm.state.ternTooltip = null; |
|||
if (!tip.parentNode) return; |
|||
cm.off("cursorActivity", clear); |
|||
cm.off('blur', clear); |
|||
cm.off('scroll', clear); |
|||
fadeOut(tip); |
|||
} |
|||
var mouseOnTip = false, old = false; |
|||
CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; }); |
|||
CodeMirror.on(tip, "mouseout", function(e) { |
|||
if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) { |
|||
if (old) clear(); |
|||
else mouseOnTip = false; |
|||
} |
|||
}); |
|||
setTimeout(maybeClear, 1700); |
|||
cm.on("cursorActivity", clear); |
|||
cm.on('blur', clear); |
|||
cm.on('scroll', clear); |
|||
} |
|||
|
|||
function makeTooltip(x, y, content) { |
|||
var node = elt("div", cls + "tooltip", content); |
|||
node.style.left = x + "px"; |
|||
node.style.top = y + "px"; |
|||
document.body.appendChild(node); |
|||
return node; |
|||
} |
|||
|
|||
function remove(node) { |
|||
var p = node && node.parentNode; |
|||
if (p) p.removeChild(node); |
|||
} |
|||
|
|||
function fadeOut(tooltip) { |
|||
tooltip.style.opacity = "0"; |
|||
setTimeout(function() { remove(tooltip); }, 1100); |
|||
} |
|||
|
|||
function showError(ts, cm, msg) { |
|||
if (ts.options.showError) |
|||
ts.options.showError(cm, msg); |
|||
else |
|||
tempTooltip(cm, String(msg)); |
|||
} |
|||
|
|||
function closeArgHints(ts) { |
|||
if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; } |
|||
} |
|||
|
|||
function docValue(ts, doc) { |
|||
var val = doc.doc.getValue(); |
|||
if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc); |
|||
return val; |
|||
} |
|||
|
|||
// Worker wrapper
|
|||
|
|||
function WorkerServer(ts) { |
|||
var worker = ts.worker = new Worker(ts.options.workerScript); |
|||
worker.postMessage({type: "init", |
|||
defs: ts.options.defs, |
|||
plugins: ts.options.plugins, |
|||
scripts: ts.options.workerDeps}); |
|||
var msgId = 0, pending = {}; |
|||
|
|||
function send(data, c) { |
|||
if (c) { |
|||
data.id = ++msgId; |
|||
pending[msgId] = c; |
|||
} |
|||
worker.postMessage(data); |
|||
} |
|||
worker.onmessage = function(e) { |
|||
var data = e.data; |
|||
if (data.type == "getFile") { |
|||
getFile(ts, data.name, function(err, text) { |
|||
send({type: "getFile", err: String(err), text: text, id: data.id}); |
|||
}); |
|||
} else if (data.type == "debug") { |
|||
window.console.log(data.message); |
|||
} else if (data.id && pending[data.id]) { |
|||
pending[data.id](data.err, data.body); |
|||
delete pending[data.id]; |
|||
} |
|||
}; |
|||
worker.onerror = function(e) { |
|||
for (var id in pending) pending[id](e); |
|||
pending = {}; |
|||
}; |
|||
|
|||
this.addFile = function(name, text) { send({type: "add", name: name, text: text}); }; |
|||
this.delFile = function(name) { send({type: "del", name: name}); }; |
|||
this.request = function(body, c) { send({type: "req", body: body}, c); }; |
|||
} |
|||
}); |
@ -0,0 +1,994 @@ |
|||
// The Tern server object
|
|||
|
|||
// A server is a stateful object that manages the analysis for a
|
|||
// project, and defines an interface for querying the code in the
|
|||
// project.
|
|||
|
|||
(function(root, mod) { |
|||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|||
return mod(exports, require("./infer"), require("./signal"), |
|||
require("acorn/acorn"), require("acorn/util/walk")); |
|||
if (typeof define == "function" && define.amd) // AMD
|
|||
return define(["exports", "./infer", "./signal", "acorn/acorn", "acorn/util/walk"], mod); |
|||
mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acorn.walk); // Plain browser env
|
|||
})(this, function(exports, infer, signal, acorn, walk) { |
|||
"use strict"; |
|||
|
|||
var plugins = Object.create(null); |
|||
exports.registerPlugin = function(name, init) { plugins[name] = init; }; |
|||
|
|||
var defaultOptions = exports.defaultOptions = { |
|||
debug: false, |
|||
async: false, |
|||
getFile: function(_f, c) { if (this.async) c(null, null); }, |
|||
defs: [], |
|||
plugins: {}, |
|||
fetchTimeout: 1000, |
|||
dependencyBudget: 20000, |
|||
reuseInstances: true, |
|||
stripCRs: false |
|||
}; |
|||
|
|||
var queryTypes = { |
|||
completions: { |
|||
takesFile: true, |
|||
run: findCompletions |
|||
}, |
|||
properties: { |
|||
run: findProperties |
|||
}, |
|||
type: { |
|||
takesFile: true, |
|||
run: findTypeAt |
|||
}, |
|||
documentation: { |
|||
takesFile: true, |
|||
run: findDocs |
|||
}, |
|||
definition: { |
|||
takesFile: true, |
|||
run: findDef |
|||
}, |
|||
refs: { |
|||
takesFile: true, |
|||
fullFile: true, |
|||
run: findRefs |
|||
}, |
|||
rename: { |
|||
takesFile: true, |
|||
fullFile: true, |
|||
run: buildRename |
|||
}, |
|||
files: { |
|||
run: listFiles |
|||
} |
|||
}; |
|||
|
|||
exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; }; |
|||
|
|||
function File(name, parent) { |
|||
this.name = name; |
|||
this.parent = parent; |
|||
this.scope = this.text = this.ast = this.lineOffsets = null; |
|||
} |
|||
File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); }; |
|||
|
|||
function updateText(file, text, srv) { |
|||
file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text; |
|||
infer.withContext(srv.cx, function() { |
|||
file.ast = infer.parse(file.text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true}); |
|||
}); |
|||
file.lineOffsets = null; |
|||
} |
|||
|
|||
var Server = exports.Server = function(options) { |
|||
this.cx = null; |
|||
this.options = options || {}; |
|||
for (var o in defaultOptions) if (!options.hasOwnProperty(o)) |
|||
options[o] = defaultOptions[o]; |
|||
|
|||
this.handlers = Object.create(null); |
|||
this.files = []; |
|||
this.fileMap = Object.create(null); |
|||
this.needsPurge = []; |
|||
this.budgets = Object.create(null); |
|||
this.uses = 0; |
|||
this.pending = 0; |
|||
this.asyncError = null; |
|||
this.passes = Object.create(null); |
|||
|
|||
this.defs = options.defs.slice(0); |
|||
for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin) && plugin in plugins) { |
|||
var init = plugins[plugin](this, options.plugins[plugin]); |
|||
if (init && init.defs) { |
|||
if (init.loadFirst) this.defs.unshift(init.defs); |
|||
else this.defs.push(init.defs); |
|||
} |
|||
if (init && init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type)) |
|||
(this.passes[type] || (this.passes[type] = [])).push(init.passes[type]); |
|||
} |
|||
|
|||
this.reset(); |
|||
}; |
|||
Server.prototype = signal.mixin({ |
|||
addFile: function(name, /*optional*/ text, parent) { |
|||
// Don't crash when sloppy plugins pass non-existent parent ids
|
|||
if (parent && !(parent in this.fileMap)) parent = null; |
|||
ensureFile(this, name, parent, text); |
|||
}, |
|||
delFile: function(name) { |
|||
var file = this.findFile(name); |
|||
if (file) { |
|||
this.needsPurge.push(file.name); |
|||
this.files.splice(this.files.indexOf(file), 1); |
|||
delete this.fileMap[name]; |
|||
} |
|||
}, |
|||
reset: function() { |
|||
this.signal("reset"); |
|||
this.cx = new infer.Context(this.defs, this); |
|||
this.uses = 0; |
|||
this.budgets = Object.create(null); |
|||
for (var i = 0; i < this.files.length; ++i) { |
|||
var file = this.files[i]; |
|||
file.scope = null; |
|||
} |
|||
}, |
|||
|
|||
request: function(doc, c) { |
|||
var inv = invalidDoc(doc); |
|||
if (inv) return c(inv); |
|||
|
|||
var self = this; |
|||
doRequest(this, doc, function(err, data) { |
|||
c(err, data); |
|||
if (self.uses > 40) { |
|||
self.reset(); |
|||
analyzeAll(self, null, function(){}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
findFile: function(name) { |
|||
return this.fileMap[name]; |
|||
}, |
|||
|
|||
flush: function(c) { |
|||
var cx = this.cx; |
|||
analyzeAll(this, null, function(err) { |
|||
if (err) return c(err); |
|||
infer.withContext(cx, c); |
|||
}); |
|||
}, |
|||
|
|||
startAsyncAction: function() { |
|||
++this.pending; |
|||
}, |
|||
finishAsyncAction: function(err) { |
|||
if (err) this.asyncError = err; |
|||
if (--this.pending === 0) this.signal("everythingFetched"); |
|||
} |
|||
}); |
|||
|
|||
function doRequest(srv, doc, c) { |
|||
if (doc.query && !queryTypes.hasOwnProperty(doc.query.type)) |
|||
return c("No query type '" + doc.query.type + "' defined"); |
|||
|
|||
var query = doc.query; |
|||
// Respond as soon as possible when this just uploads files
|
|||
if (!query) c(null, {}); |
|||
|
|||
var files = doc.files || []; |
|||
if (files.length) ++srv.uses; |
|||
for (var i = 0; i < files.length; ++i) { |
|||
var file = files[i]; |
|||
if (file.type == "delete") |
|||
srv.delFile(file.name); |
|||
else |
|||
ensureFile(srv, file.name, null, file.type == "full" ? file.text : null); |
|||
} |
|||
|
|||
var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null; |
|||
if (!query) { |
|||
analyzeAll(srv, timeBudget, function(){}); |
|||
return; |
|||
} |
|||
|
|||
var queryType = queryTypes[query.type]; |
|||
if (queryType.takesFile) { |
|||
if (typeof query.file != "string") return c(".query.file must be a string"); |
|||
if (!/^#/.test(query.file)) ensureFile(srv, query.file, null); |
|||
} |
|||
|
|||
analyzeAll(srv, timeBudget, function(err) { |
|||
if (err) return c(err); |
|||
var file = queryType.takesFile && resolveFile(srv, files, query.file); |
|||
if (queryType.fullFile && file.type == "part") |
|||
return c("Can't run a " + query.type + " query on a file fragment"); |
|||
|
|||
function run() { |
|||
var result; |
|||
try { |
|||
result = queryType.run(srv, query, file); |
|||
} catch (e) { |
|||
if (srv.options.debug && e.name != "TernError") console.error(e.stack); |
|||
return c(e); |
|||
} |
|||
c(null, result); |
|||
} |
|||
infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run); |
|||
}); |
|||
} |
|||
|
|||
function analyzeFile(srv, file) { |
|||
infer.withContext(srv.cx, function() { |
|||
file.scope = srv.cx.topScope; |
|||
srv.signal("beforeLoad", file); |
|||
infer.analyze(file.ast, file.name, file.scope, srv.passes); |
|||
srv.signal("afterLoad", file); |
|||
}); |
|||
return file; |
|||
} |
|||
|
|||
function ensureFile(srv, name, parent, text) { |
|||
var known = srv.findFile(name); |
|||
if (known) { |
|||
if (text != null) { |
|||
if (known.scope) { |
|||
srv.needsPurge.push(name); |
|||
known.scope = null; |
|||
} |
|||
updateText(known, text, srv); |
|||
} |
|||
if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) { |
|||
known.parent = parent; |
|||
if (known.excluded) known.excluded = null; |
|||
} |
|||
return; |
|||
} |
|||
|
|||
var file = new File(name, parent); |
|||
srv.files.push(file); |
|||
srv.fileMap[name] = file; |
|||
if (text != null) { |
|||
updateText(file, text, srv); |
|||
} else if (srv.options.async) { |
|||
srv.startAsyncAction(); |
|||
srv.options.getFile(name, function(err, text) { |
|||
updateText(file, text || "", srv); |
|||
srv.finishAsyncAction(err); |
|||
}); |
|||
} else { |
|||
updateText(file, srv.options.getFile(name) || "", srv); |
|||
} |
|||
} |
|||
|
|||
function fetchAll(srv, c) { |
|||
var done = true, returned = false; |
|||
srv.files.forEach(function(file) { |
|||
if (file.text != null) return; |
|||
if (srv.options.async) { |
|||
done = false; |
|||
srv.options.getFile(file.name, function(err, text) { |
|||
if (err && !returned) { returned = true; return c(err); } |
|||
updateText(file, text || "", srv); |
|||
fetchAll(srv, c); |
|||
}); |
|||
} else { |
|||
try { |
|||
updateText(file, srv.options.getFile(file.name) || "", srv); |
|||
} catch (e) { return c(e); } |
|||
} |
|||
}); |
|||
if (done) c(); |
|||
} |
|||
|
|||
function waitOnFetch(srv, timeBudget, c) { |
|||
var done = function() { |
|||
srv.off("everythingFetched", done); |
|||
clearTimeout(timeout); |
|||
analyzeAll(srv, timeBudget, c); |
|||
}; |
|||
srv.on("everythingFetched", done); |
|||
var timeout = setTimeout(done, srv.options.fetchTimeout); |
|||
} |
|||
|
|||
function analyzeAll(srv, timeBudget, c) { |
|||
if (srv.pending) return waitOnFetch(srv, timeBudget, c); |
|||
|
|||
var e = srv.fetchError; |
|||
if (e) { srv.fetchError = null; return c(e); } |
|||
|
|||
if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() { |
|||
infer.purge(srv.needsPurge); |
|||
srv.needsPurge.length = 0; |
|||
}); |
|||
|
|||
var done = true; |
|||
// The second inner loop might add new files. The outer loop keeps
|
|||
// repeating both inner loops until all files have been looked at.
|
|||
for (var i = 0; i < srv.files.length;) { |
|||
var toAnalyze = []; |
|||
for (; i < srv.files.length; ++i) { |
|||
var file = srv.files[i]; |
|||
if (file.text == null) done = false; |
|||
else if (file.scope == null && !file.excluded) toAnalyze.push(file); |
|||
} |
|||
toAnalyze.sort(function(a, b) { |
|||
return parentDepth(srv, a.parent) - parentDepth(srv, b.parent); |
|||
}); |
|||
for (var j = 0; j < toAnalyze.length; j++) { |
|||
var file = toAnalyze[j]; |
|||
if (file.parent && !chargeOnBudget(srv, file)) { |
|||
file.excluded = true; |
|||
} else if (timeBudget) { |
|||
var startTime = +new Date; |
|||
infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); }); |
|||
timeBudget[0] -= +new Date - startTime; |
|||
} else { |
|||
analyzeFile(srv, file); |
|||
} |
|||
} |
|||
} |
|||
if (done) c(); |
|||
else waitOnFetch(srv, timeBudget, c); |
|||
} |
|||
|
|||
function firstLine(str) { |
|||
var end = str.indexOf("\n"); |
|||
if (end < 0) return str; |
|||
return str.slice(0, end); |
|||
} |
|||
|
|||
function findMatchingPosition(line, file, near) { |
|||
var pos = Math.max(0, near - 500), closest = null; |
|||
if (!/^\s*$/.test(line)) for (;;) { |
|||
var found = file.indexOf(line, pos); |
|||
if (found < 0 || found > near + 500) break; |
|||
if (closest == null || Math.abs(closest - near) > Math.abs(found - near)) |
|||
closest = found; |
|||
pos = found + line.length; |
|||
} |
|||
return closest; |
|||
} |
|||
|
|||
function scopeDepth(s) { |
|||
for (var i = 0; s; ++i, s = s.prev) {} |
|||
return i; |
|||
} |
|||
|
|||
function ternError(msg) { |
|||
var err = new Error(msg); |
|||
err.name = "TernError"; |
|||
return err; |
|||
} |
|||
|
|||
function resolveFile(srv, localFiles, name) { |
|||
var isRef = name.match(/^#(\d+)$/); |
|||
if (!isRef) return srv.findFile(name); |
|||
|
|||
var file = localFiles[isRef[1]]; |
|||
if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name); |
|||
if (file.type == "full") return srv.findFile(file.name); |
|||
|
|||
// This is a partial file
|
|||
|
|||
var realFile = file.backing = srv.findFile(file.name); |
|||
var offset = file.offset; |
|||
if (file.offsetLines) offset = {line: file.offsetLines, ch: 0}; |
|||
file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true); |
|||
var line = firstLine(file.text); |
|||
var foundPos = findMatchingPosition(line, realFile.text, offset); |
|||
var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos; |
|||
var inObject, atFunction; |
|||
|
|||
infer.withContext(srv.cx, function() { |
|||
infer.purge(file.name, pos, pos + file.text.length); |
|||
|
|||
var text = file.text, m; |
|||
if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) { |
|||
var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression"); |
|||
if (objNode && objNode.node.objType) |
|||
inObject = {type: objNode.node.objType, prop: m[2] || m[1]}; |
|||
} |
|||
if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) { |
|||
var cut = m[1].length, white = ""; |
|||
for (var i = 0; i < cut; ++i) white += " "; |
|||
text = white + text.slice(cut); |
|||
atFunction = true; |
|||
} |
|||
|
|||
var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope); |
|||
var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope); |
|||
var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart; |
|||
file.ast = infer.parse(text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true}); |
|||
infer.analyze(file.ast, file.name, scope, srv.passes); |
|||
|
|||
// This is a kludge to tie together the function types (if any)
|
|||
// outside and inside of the fragment, so that arguments and
|
|||
// return values have some information known about them.
|
|||
tieTogether: if (inObject || atFunction) { |
|||
var newInner = infer.scopeAt(file.ast, line.length, scopeStart); |
|||
if (!newInner.fnType) break tieTogether; |
|||
if (inObject) { |
|||
var prop = inObject.type.getProp(inObject.prop); |
|||
prop.addType(newInner.fnType); |
|||
} else if (atFunction) { |
|||
var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope); |
|||
if (inner == scopeStart || !inner.fnType) break tieTogether; |
|||
var fOld = inner.fnType, fNew = newInner.fnType; |
|||
if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether; |
|||
for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i) |
|||
fOld.args[i].propagate(fNew.args[i]); |
|||
fOld.self.propagate(fNew.self); |
|||
fNew.retval.propagate(fOld.retval); |
|||
} |
|||
} |
|||
}); |
|||
return file; |
|||
} |
|||
|
|||
// Budget management
|
|||
|
|||
function astSize(node) { |
|||
var size = 0; |
|||
walk.simple(node, {Expression: function() { ++size; }}); |
|||
return size; |
|||
} |
|||
|
|||
function parentDepth(srv, parent) { |
|||
var depth = 0; |
|||
while (parent) { |
|||
parent = srv.findFile(parent).parent; |
|||
++depth; |
|||
} |
|||
return depth; |
|||
} |
|||
|
|||
function budgetName(srv, file) { |
|||
for (;;) { |
|||
var parent = srv.findFile(file.parent); |
|||
if (!parent.parent) break; |
|||
file = parent; |
|||
} |
|||
return file.name; |
|||
} |
|||
|
|||
function chargeOnBudget(srv, file) { |
|||
var bName = budgetName(srv, file); |
|||
var size = astSize(file.ast); |
|||
var known = srv.budgets[bName]; |
|||
if (known == null) |
|||
known = srv.budgets[bName] = srv.options.dependencyBudget; |
|||
if (known < size) return false; |
|||
srv.budgets[bName] = known - size; |
|||
return true; |
|||
} |
|||
|
|||
// Query helpers
|
|||
|
|||
function isPosition(val) { |
|||
return typeof val == "number" || typeof val == "object" && |
|||
typeof val.line == "number" && typeof val.ch == "number"; |
|||
} |
|||
|
|||
// Baseline query document validation
|
|||
function invalidDoc(doc) { |
|||
if (doc.query) { |
|||
if (typeof doc.query.type != "string") return ".query.type must be a string"; |
|||
if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position"; |
|||
if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position"; |
|||
} |
|||
if (doc.files) { |
|||
if (!Array.isArray(doc.files)) return "Files property must be an array"; |
|||
for (var i = 0; i < doc.files.length; ++i) { |
|||
var file = doc.files[i]; |
|||
if (typeof file != "object") return ".files[n] must be objects"; |
|||
else if (typeof file.name != "string") return ".files[n].name must be a string"; |
|||
else if (file.type == "delete") continue; |
|||
else if (typeof file.text != "string") return ".files[n].text must be a string"; |
|||
else if (file.type == "part") { |
|||
if (!isPosition(file.offset) && typeof file.offsetLines != "number") |
|||
return ".files[n].offset must be a position"; |
|||
} else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\""; |
|||
} |
|||
} |
|||
} |
|||
|
|||
var offsetSkipLines = 25; |
|||
|
|||
function findLineStart(file, line) { |
|||
var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]); |
|||
var pos = 0, curLine = 0; |
|||
var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1); |
|||
var pos = offsets[storePos], curLine = storePos * offsetSkipLines; |
|||
|
|||
while (curLine < line) { |
|||
++curLine; |
|||
pos = text.indexOf("\n", pos) + 1; |
|||
if (pos === 0) return null; |
|||
if (curLine % offsetSkipLines === 0) offsets.push(pos); |
|||
} |
|||
return pos; |
|||
} |
|||
|
|||
var resolvePos = exports.resolvePos = function(file, pos, tolerant) { |
|||
if (typeof pos != "number") { |
|||
var lineStart = findLineStart(file, pos.line); |
|||
if (lineStart == null) { |
|||
if (tolerant) pos = file.text.length; |
|||
else throw ternError("File doesn't contain a line " + pos.line); |
|||
} else { |
|||
pos = lineStart + pos.ch; |
|||
} |
|||
} |
|||
if (pos > file.text.length) { |
|||
if (tolerant) pos = file.text.length; |
|||
else throw ternError("Position " + pos + " is outside of file."); |
|||
} |
|||
return pos; |
|||
}; |
|||
|
|||
function asLineChar(file, pos) { |
|||
if (!file) return {line: 0, ch: 0}; |
|||
var offsets = file.lineOffsets || (file.lineOffsets = [0]); |
|||
var text = file.text, line, lineStart; |
|||
for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) { |
|||
line = i * offsetSkipLines; |
|||
lineStart = offsets[i]; |
|||
} |
|||
for (;;) { |
|||
var eol = text.indexOf("\n", lineStart); |
|||
if (eol >= pos || eol < 0) break; |
|||
lineStart = eol + 1; |
|||
++line; |
|||
} |
|||
return {line: line, ch: pos - lineStart}; |
|||
} |
|||
|
|||
var outputPos = exports.outputPos = function(query, file, pos) { |
|||
if (query.lineCharPositions) { |
|||
var out = asLineChar(file, pos); |
|||
if (file.type == "part") |
|||
out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line; |
|||
return out; |
|||
} else { |
|||
return pos + (file.type == "part" ? file.offset : 0); |
|||
} |
|||
}; |
|||
|
|||
// Delete empty fields from result objects
|
|||
function clean(obj) { |
|||
for (var prop in obj) if (obj[prop] == null) delete obj[prop]; |
|||
return obj; |
|||
} |
|||
function maybeSet(obj, prop, val) { |
|||
if (val != null) obj[prop] = val; |
|||
} |
|||
|
|||
// Built-in query types
|
|||
|
|||
function compareCompletions(a, b) { |
|||
if (typeof a != "string") { a = a.name; b = b.name; } |
|||
var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b); |
|||
if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1; |
|||
else return aUp ? 1 : -1; |
|||
} |
|||
|
|||
function isStringAround(node, start, end) { |
|||
return node.type == "Literal" && typeof node.value == "string" && |
|||
node.start == start - 1 && node.end <= end + 1; |
|||
} |
|||
|
|||
function pointInProp(objNode, point) { |
|||
for (var i = 0; i < objNode.properties.length; i++) { |
|||
var curProp = objNode.properties[i]; |
|||
if (curProp.key.start <= point && curProp.key.end >= point) |
|||
return curProp; |
|||
} |
|||
} |
|||
|
|||
var jsKeywords = ("break do instanceof typeof case else new var " + |
|||
"catch finally return void continue for switch while debugger " + |
|||
"function this with default if throw delete in try").split(" "); |
|||
|
|||
function findCompletions(srv, query, file) { |
|||
if (query.end == null) throw ternError("missing .query.end field"); |
|||
if (srv.passes.completion) for (var i = 0; i < srv.passes.completion.length; i++) { |
|||
var result = srv.passes.completion[i](file, query); |
|||
if (result) return result; |
|||
} |
|||
|
|||
var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text; |
|||
while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart; |
|||
if (query.expandWordForward !== false) |
|||
while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd; |
|||
var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj; |
|||
if (query.caseInsensitive) word = word.toLowerCase(); |
|||
var wrapAsObjs = query.types || query.depths || query.docs || query.urls || query.origins; |
|||
|
|||
function gather(prop, obj, depth, addInfo) { |
|||
// 'hasOwnProperty' and such are usually just noise, leave them
|
|||
// out when no prefix is provided.
|
|||
if (query.omitObjectPrototype !== false && obj == srv.cx.protos.Object && !word) return; |
|||
if (query.filter !== false && word && |
|||
(query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return; |
|||
if (ignoreObj && ignoreObj.props[prop]) return; |
|||
for (var i = 0; i < completions.length; ++i) { |
|||
var c = completions[i]; |
|||
if ((wrapAsObjs ? c.name : c) == prop) return; |
|||
} |
|||
var rec = wrapAsObjs ? {name: prop} : prop; |
|||
completions.push(rec); |
|||
|
|||
if (obj && (query.types || query.docs || query.urls || query.origins)) { |
|||
var val = obj.props[prop]; |
|||
infer.resetGuessing(); |
|||
var type = val.getType(); |
|||
rec.guess = infer.didGuess(); |
|||
if (query.types) |
|||
rec.type = infer.toString(val); |
|||
if (query.docs) |
|||
maybeSet(rec, "doc", val.doc || type && type.doc); |
|||
if (query.urls) |
|||
maybeSet(rec, "url", val.url || type && type.url); |
|||
if (query.origins) |
|||
maybeSet(rec, "origin", val.origin || type && type.origin); |
|||
} |
|||
if (query.depths) rec.depth = depth; |
|||
if (wrapAsObjs && addInfo) addInfo(rec); |
|||
} |
|||
|
|||
var hookname, prop, objType, isKey; |
|||
|
|||
var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope); |
|||
var memberExpr, objLit; |
|||
// Decide whether this is an object property, either in a member
|
|||
// expression or an object literal.
|
|||
if (exprAt) { |
|||
if (exprAt.node.type == "MemberExpression" && exprAt.node.object.end < wordStart) { |
|||
memberExpr = exprAt; |
|||
} else if (isStringAround(exprAt.node, wordStart, wordEnd)) { |
|||
var parent = infer.parentNode(exprAt.node, file.ast); |
|||
if (parent.type == "MemberExpression" && parent.property == exprAt.node) |
|||
memberExpr = {node: parent, state: exprAt.state}; |
|||
} else if (exprAt.node.type == "ObjectExpression") { |
|||
var objProp = pointInProp(exprAt.node, wordEnd); |
|||
if (objProp) { |
|||
objLit = exprAt; |
|||
prop = isKey = objProp.key.name; |
|||
} else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) { |
|||
objLit = exprAt; |
|||
prop = isKey = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (objLit) { |
|||
// Since we can't use the type of the literal itself to complete
|
|||
// its properties (it doesn't contain the information we need),
|
|||
// we have to try asking the surrounding expression for type info.
|
|||
objType = infer.typeFromContext(file.ast, objLit); |
|||
ignoreObj = objLit.node.objType; |
|||
} else if (memberExpr) { |
|||
prop = memberExpr.node.property; |
|||
prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name; |
|||
memberExpr.node = memberExpr.node.object; |
|||
objType = infer.expressionType(memberExpr); |
|||
} else if (text.charAt(wordStart - 1) == ".") { |
|||
var pathStart = wordStart - 1; |
|||
while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--; |
|||
var path = text.slice(pathStart, wordStart - 1); |
|||
if (path) { |
|||
objType = infer.def.parsePath(path, file.scope).getObjType(); |
|||
prop = word; |
|||
} |
|||
} |
|||
|
|||
if (prop != null) { |
|||
srv.cx.completingProperty = prop; |
|||
|
|||
if (objType) infer.forAllPropertiesOf(objType, gather); |
|||
|
|||
if (!completions.length && query.guess !== false && objType && objType.guessProperties) |
|||
objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖") gather(p, o, d);}); |
|||
if (!completions.length && word.length >= 2 && query.guess !== false) |
|||
for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0); |
|||
hookname = "memberCompletion"; |
|||
} else { |
|||
infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather); |
|||
if (query.includeKeywords) jsKeywords.forEach(function(kw) { |
|||
gather(kw, null, 0, function(rec) { rec.isKeyword = true; }); |
|||
}); |
|||
hookname = "variableCompletion"; |
|||
} |
|||
if (srv.passes[hookname]) |
|||
srv.passes[hookname].forEach(function(hook) {hook(file, wordStart, wordEnd, gather);}); |
|||
|
|||
if (query.sort !== false) completions.sort(compareCompletions); |
|||
srv.cx.completingProperty = null; |
|||
|
|||
return {start: outputPos(query, file, wordStart), |
|||
end: outputPos(query, file, wordEnd), |
|||
isProperty: !!prop, |
|||
isObjectKey: !!isKey, |
|||
completions: completions}; |
|||
} |
|||
|
|||
function findProperties(srv, query) { |
|||
var prefix = query.prefix, found = []; |
|||
for (var prop in srv.cx.props) |
|||
if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop); |
|||
if (query.sort !== false) found.sort(compareCompletions); |
|||
return {completions: found}; |
|||
} |
|||
|
|||
var findExpr = exports.findQueryExpr = function(file, query, wide) { |
|||
if (query.end == null) throw ternError("missing .query.end field"); |
|||
|
|||
if (query.variable) { |
|||
var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope); |
|||
return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1}, |
|||
state: scope}; |
|||
} else { |
|||
var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end); |
|||
var expr = infer.findExpressionAt(file.ast, start, end, file.scope); |
|||
if (expr) return expr; |
|||
expr = infer.findExpressionAround(file.ast, start, end, file.scope); |
|||
if (expr && (expr.node.type == "ObjectExpression" || wide || |
|||
(start == null ? end : start) - expr.node.start < 20 || expr.node.end - end < 20)) |
|||
return expr; |
|||
return null; |
|||
} |
|||
}; |
|||
|
|||
function findExprOrThrow(file, query, wide) { |
|||
var expr = findExpr(file, query, wide); |
|||
if (expr) return expr; |
|||
throw ternError("No expression at the given position."); |
|||
} |
|||
|
|||
function ensureObj(tp) { |
|||
if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null; |
|||
return tp; |
|||
} |
|||
|
|||
function findExprType(srv, query, file, expr) { |
|||
var type; |
|||
if (expr) { |
|||
infer.resetGuessing(); |
|||
type = infer.expressionType(expr); |
|||
} |
|||
if (srv.passes["typeAt"]) { |
|||
var pos = resolvePos(file, query.end); |
|||
srv.passes["typeAt"].forEach(function(hook) { |
|||
type = hook(file, pos, expr, type); |
|||
}); |
|||
} |
|||
if (!type) throw ternError("No type found at the given position."); |
|||
|
|||
var objProp; |
|||
if (expr.node.type == "ObjectExpression" && query.end != null && |
|||
(objProp = pointInProp(expr.node, resolvePos(file, query.end)))) { |
|||
var name = objProp.key.name; |
|||
var fromCx = ensureObj(infer.typeFromContext(file.ast, expr)); |
|||
if (fromCx && fromCx.hasProp(name)) { |
|||
type = fromCx.hasProp(name); |
|||
} else { |
|||
var fromLocal = ensureObj(type); |
|||
if (fromLocal && fromLocal.hasProp(name)) |
|||
type = fromLocal.hasProp(name); |
|||
} |
|||
} |
|||
return type; |
|||
}; |
|||
|
|||
function findTypeAt(srv, query, file) { |
|||
var expr = findExpr(file, query), exprName; |
|||
var type = findExprType(srv, query, file, expr), exprType = type; |
|||
if (query.preferFunction) |
|||
type = type.getFunctionType() || type.getType(); |
|||
else |
|||
type = type.getType(); |
|||
|
|||
if (expr) { |
|||
if (expr.node.type == "Identifier") |
|||
exprName = expr.node.name; |
|||
else if (expr.node.type == "MemberExpression" && !expr.node.computed) |
|||
exprName = expr.node.property.name; |
|||
} |
|||
|
|||
if (query.depth != null && typeof query.depth != "number") |
|||
throw ternError(".query.depth must be a number"); |
|||
|
|||
var result = {guess: infer.didGuess(), |
|||
type: infer.toString(exprType, query.depth), |
|||
name: type && type.name, |
|||
exprName: exprName}; |
|||
if (type) storeTypeDocs(type, result); |
|||
if (!result.doc && exprType.doc) result.doc = exprType.doc; |
|||
|
|||
return clean(result); |
|||
} |
|||
|
|||
function findDocs(srv, query, file) { |
|||
var expr = findExpr(file, query); |
|||
var type = findExprType(srv, query, file, expr); |
|||
var result = {url: type.url, doc: type.doc, type: infer.toString(type)}; |
|||
var inner = type.getType(); |
|||
if (inner) storeTypeDocs(inner, result); |
|||
return clean(result); |
|||
} |
|||
|
|||
function storeTypeDocs(type, out) { |
|||
if (!out.url) out.url = type.url; |
|||
if (!out.doc) out.doc = type.doc; |
|||
if (!out.origin) out.origin = type.origin; |
|||
var ctor, boring = infer.cx().protos; |
|||
if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) && |
|||
type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array) { |
|||
out.url = ctor.url; |
|||
out.doc = ctor.doc; |
|||
} |
|||
} |
|||
|
|||
var getSpan = exports.getSpan = function(obj) { |
|||
if (!obj.origin) return; |
|||
if (obj.originNode) { |
|||
var node = obj.originNode; |
|||
if (/^Function/.test(node.type) && node.id) node = node.id; |
|||
return {origin: obj.origin, node: node}; |
|||
} |
|||
if (obj.span) return {origin: obj.origin, span: obj.span}; |
|||
}; |
|||
|
|||
var storeSpan = exports.storeSpan = function(srv, query, span, target) { |
|||
target.origin = span.origin; |
|||
if (span.span) { |
|||
var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span); |
|||
target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]); |
|||
target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]); |
|||
} else { |
|||
var file = srv.findFile(span.origin); |
|||
target.start = outputPos(query, file, span.node.start); |
|||
target.end = outputPos(query, file, span.node.end); |
|||
} |
|||
}; |
|||
|
|||
function findDef(srv, query, file) { |
|||
var expr = findExpr(file, query); |
|||
var type = findExprType(srv, query, file, expr); |
|||
if (infer.didGuess()) return {}; |
|||
|
|||
var span = getSpan(type); |
|||
var result = {url: type.url, doc: type.doc, origin: type.origin}; |
|||
|
|||
if (type.types) for (var i = type.types.length - 1; i >= 0; --i) { |
|||
var tp = type.types[i]; |
|||
storeTypeDocs(tp, result); |
|||
if (!span) span = getSpan(tp); |
|||
} |
|||
|
|||
if (span && span.node) { // refers to a loaded file
|
|||
var spanFile = span.node.sourceFile || srv.findFile(span.origin); |
|||
var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end); |
|||
result.start = start; result.end = end; |
|||
result.file = span.origin; |
|||
var cxStart = Math.max(0, span.node.start - 50); |
|||
result.contextOffset = span.node.start - cxStart; |
|||
result.context = spanFile.text.slice(cxStart, cxStart + 50); |
|||
} else if (span) { // external
|
|||
result.file = span.origin; |
|||
storeSpan(srv, query, span, result); |
|||
} |
|||
return clean(result); |
|||
} |
|||
|
|||
function findRefsToVariable(srv, query, file, expr, checkShadowing) { |
|||
var name = expr.node.name; |
|||
|
|||
for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {} |
|||
if (!scope) throw ternError("Could not find a definition for " + name + " " + !!srv.cx.topScope.props.x); |
|||
|
|||
var type, refs = []; |
|||
function storeRef(file) { |
|||
return function(node, scopeHere) { |
|||
if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) { |
|||
var exists = s.hasProp(checkShadowing); |
|||
if (exists) |
|||
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " + |
|||
(asLineChar(file, node.start).line + 1) + " point to the definition at line " + |
|||
(asLineChar(file, exists.name.start).line + 1)); |
|||
} |
|||
refs.push({file: file.name, |
|||
start: outputPos(query, file, node.start), |
|||
end: outputPos(query, file, node.end)}); |
|||
}; |
|||
} |
|||
|
|||
if (scope.originNode) { |
|||
type = "local"; |
|||
if (checkShadowing) { |
|||
for (var prev = scope.prev; prev; prev = prev.prev) |
|||
if (checkShadowing in prev.props) break; |
|||
if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) { |
|||
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " + |
|||
(asLineChar(file, node.start).line + 1)); |
|||
}); |
|||
} |
|||
infer.findRefs(scope.originNode, scope, name, scope, storeRef(file)); |
|||
} else { |
|||
type = "global"; |
|||
for (var i = 0; i < srv.files.length; ++i) { |
|||
var cur = srv.files[i]; |
|||
infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur)); |
|||
} |
|||
} |
|||
|
|||
return {refs: refs, type: type, name: name}; |
|||
} |
|||
|
|||
function findRefsToProperty(srv, query, expr, prop) { |
|||
var objType = infer.expressionType(expr).getObjType(); |
|||
if (!objType) throw ternError("Couldn't determine type of base object."); |
|||
|
|||
var refs = []; |
|||
function storeRef(file) { |
|||
return function(node) { |
|||
refs.push({file: file.name, |
|||
start: outputPos(query, file, node.start), |
|||
end: outputPos(query, file, node.end)}); |
|||
}; |
|||
} |
|||
for (var i = 0; i < srv.files.length; ++i) { |
|||
var cur = srv.files[i]; |
|||
infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur)); |
|||
} |
|||
|
|||
return {refs: refs, name: prop.name}; |
|||
} |
|||
|
|||
function findRefs(srv, query, file) { |
|||
var expr = findExprOrThrow(file, query, true); |
|||
if (expr && expr.node.type == "Identifier") { |
|||
return findRefsToVariable(srv, query, file, expr); |
|||
} else if (expr && expr.node.type == "MemberExpression" && !expr.node.computed) { |
|||
var p = expr.node.property; |
|||
expr.node = expr.node.object; |
|||
return findRefsToProperty(srv, query, expr, p); |
|||
} else if (expr && expr.node.type == "ObjectExpression") { |
|||
var pos = resolvePos(file, query.end); |
|||
for (var i = 0; i < expr.node.properties.length; ++i) { |
|||
var k = expr.node.properties[i].key; |
|||
if (k.start <= pos && k.end >= pos) |
|||
return findRefsToProperty(srv, query, expr, k); |
|||
} |
|||
} |
|||
throw ternError("Not at a variable or property name."); |
|||
} |
|||
|
|||
function buildRename(srv, query, file) { |
|||
if (typeof query.newName != "string") throw ternError(".query.newName should be a string"); |
|||
var expr = findExprOrThrow(file, query); |
|||
if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable."); |
|||
|
|||
var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs; |
|||
delete data.refs; |
|||
data.files = srv.files.map(function(f){return f.name;}); |
|||
|
|||
var changes = data.changes = []; |
|||
for (var i = 0; i < refs.length; ++i) { |
|||
var use = refs[i]; |
|||
use.text = query.newName; |
|||
changes.push(use); |
|||
} |
|||
|
|||
return data; |
|||
} |
|||
|
|||
function listFiles(srv) { |
|||
return {files: srv.files.map(function(f){return f.name;})}; |
|||
} |
|||
|
|||
exports.version = "0.9.1"; |
|||
}); |
@ -0,0 +1,359 @@ |
|||
// AST walker module for Mozilla Parser API compatible trees
|
|||
|
|||
(function(mod) { |
|||
if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
|
|||
if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
|
|||
mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
|
|||
})(function(exports) { |
|||
"use strict"; |
|||
|
|||
// A simple walk is one where you simply specify callbacks to be
|
|||
// called on specific nodes. The last two arguments are optional. A
|
|||
// simple use would be
|
|||
//
|
|||
// walk.simple(myTree, {
|
|||
// Expression: function(node) { ... }
|
|||
// });
|
|||
//
|
|||
// to do something with all expressions. All Parser API node types
|
|||
// can be used to identify node types, as well as Expression,
|
|||
// Statement, and ScopeBody, which denote categories of nodes.
|
|||
//
|
|||
// The base argument can be used to pass a custom (recursive)
|
|||
// walker, and state can be used to give this walked an initial
|
|||
// state.
|
|||
exports.simple = function(node, visitors, base, state) { |
|||
if (!base) base = exports.base; |
|||
function c(node, st, override) { |
|||
var type = override || node.type, found = visitors[type]; |
|||
base[type](node, st, c); |
|||
if (found) found(node, st); |
|||
} |
|||
c(node, state); |
|||
}; |
|||
|
|||
// An ancestor walk builds up an array of ancestor nodes (including
|
|||
// the current node) and passes them to the callback as the state parameter.
|
|||
exports.ancestor = function(node, visitors, base, state) { |
|||
if (!base) base = exports.base; |
|||
if (!state) state = []; |
|||
function c(node, st, override) { |
|||
var type = override || node.type, found = visitors[type]; |
|||
if (node != st[st.length - 1]) { |
|||
st = st.slice(); |
|||
st.push(node); |
|||
} |
|||
base[type](node, st, c); |
|||
if (found) found(node, st); |
|||
} |
|||
c(node, state); |
|||
}; |
|||
|
|||
// A recursive walk is one where your functions override the default
|
|||
// walkers. They can modify and replace the state parameter that's
|
|||
// threaded through the walk, and can opt how and whether to walk
|
|||
// their child nodes (by calling their third argument on these
|
|||
// nodes).
|
|||
exports.recursive = function(node, state, funcs, base) { |
|||
var visitor = funcs ? exports.make(funcs, base) : base; |
|||
function c(node, st, override) { |
|||
visitor[override || node.type](node, st, c); |
|||
} |
|||
c(node, state); |
|||
}; |
|||
|
|||
function makeTest(test) { |
|||
if (typeof test == "string") |
|||
return function(type) { return type == test; }; |
|||
else if (!test) |
|||
return function() { return true; }; |
|||
else |
|||
return test; |
|||
} |
|||
|
|||
function Found(node, state) { this.node = node; this.state = state; } |
|||
|
|||
// Find a node with a given start, end, and type (all are optional,
|
|||
// null can be used as wildcard). Returns a {node, state} object, or
|
|||
// undefined when it doesn't find a matching node.
|
|||
exports.findNodeAt = function(node, start, end, test, base, state) { |
|||
test = makeTest(test); |
|||
try { |
|||
if (!base) base = exports.base; |
|||
var c = function(node, st, override) { |
|||
var type = override || node.type; |
|||
if ((start == null || node.start <= start) && |
|||
(end == null || node.end >= end)) |
|||
base[type](node, st, c); |
|||
if (test(type, node) && |
|||
(start == null || node.start == start) && |
|||
(end == null || node.end == end)) |
|||
throw new Found(node, st); |
|||
}; |
|||
c(node, state); |
|||
} catch (e) { |
|||
if (e instanceof Found) return e; |
|||
throw e; |
|||
} |
|||
}; |
|||
|
|||
// Find the innermost node of a given type that contains the given
|
|||
// position. Interface similar to findNodeAt.
|
|||
exports.findNodeAround = function(node, pos, test, base, state) { |
|||
test = makeTest(test); |
|||
try { |
|||
if (!base) base = exports.base; |
|||
var c = function(node, st, override) { |
|||
var type = override || node.type; |
|||
if (node.start > pos || node.end < pos) return; |
|||
base[type](node, st, c); |
|||
if (test(type, node)) throw new Found(node, st); |
|||
}; |
|||
c(node, state); |
|||
} catch (e) { |
|||
if (e instanceof Found) return e; |
|||
throw e; |
|||
} |
|||
}; |
|||
|
|||
// Find the outermost matching node after a given position.
|
|||
exports.findNodeAfter = function(node, pos, test, base, state) { |
|||
test = makeTest(test); |
|||
try { |
|||
if (!base) base = exports.base; |
|||
var c = function(node, st, override) { |
|||
if (node.end < pos) return; |
|||
var type = override || node.type; |
|||
if (node.start >= pos && test(type, node)) throw new Found(node, st); |
|||
base[type](node, st, c); |
|||
}; |
|||
c(node, state); |
|||
} catch (e) { |
|||
if (e instanceof Found) return e; |
|||
throw e; |
|||
} |
|||
}; |
|||
|
|||
// Find the outermost matching node before a given position.
|
|||
exports.findNodeBefore = function(node, pos, test, base, state) { |
|||
test = makeTest(test); |
|||
if (!base) base = exports.base; |
|||
var max; |
|||
var c = function(node, st, override) { |
|||
if (node.start > pos) return; |
|||
var type = override || node.type; |
|||
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) |
|||
max = new Found(node, st); |
|||
base[type](node, st, c); |
|||
}; |
|||
c(node, state); |
|||
return max; |
|||
}; |
|||
|
|||
// Used to create a custom walker. Will fill in all missing node
|
|||
// type properties with the defaults.
|
|||
exports.make = function(funcs, base) { |
|||
if (!base) base = exports.base; |
|||
var visitor = {}; |
|||
for (var type in base) visitor[type] = base[type]; |
|||
for (var type in funcs) visitor[type] = funcs[type]; |
|||
return visitor; |
|||
}; |
|||
|
|||
function skipThrough(node, st, c) { c(node, st); } |
|||
function ignore(_node, _st, _c) {} |
|||
|
|||
// Node walkers.
|
|||
|
|||
var base = exports.base = {}; |
|||
base.Program = base.BlockStatement = function(node, st, c) { |
|||
for (var i = 0; i < node.body.length; ++i) |
|||
c(node.body[i], st, "Statement"); |
|||
}; |
|||
base.Statement = skipThrough; |
|||
base.EmptyStatement = ignore; |
|||
base.ExpressionStatement = base.ParenthesizedExpression = function(node, st, c) { |
|||
c(node.expression, st, "Expression"); |
|||
}; |
|||
base.IfStatement = function(node, st, c) { |
|||
c(node.test, st, "Expression"); |
|||
c(node.consequent, st, "Statement"); |
|||
if (node.alternate) c(node.alternate, st, "Statement"); |
|||
}; |
|||
base.LabeledStatement = function(node, st, c) { |
|||
c(node.body, st, "Statement"); |
|||
}; |
|||
base.BreakStatement = base.ContinueStatement = ignore; |
|||
base.WithStatement = function(node, st, c) { |
|||
c(node.object, st, "Expression"); |
|||
c(node.body, st, "Statement"); |
|||
}; |
|||
base.SwitchStatement = function(node, st, c) { |
|||
c(node.discriminant, st, "Expression"); |
|||
for (var i = 0; i < node.cases.length; ++i) { |
|||
var cs = node.cases[i]; |
|||
if (cs.test) c(cs.test, st, "Expression"); |
|||
for (var j = 0; j < cs.consequent.length; ++j) |
|||
c(cs.consequent[j], st, "Statement"); |
|||
} |
|||
}; |
|||
base.ReturnStatement = base.YieldExpression = function(node, st, c) { |
|||
if (node.argument) c(node.argument, st, "Expression"); |
|||
}; |
|||
base.ThrowStatement = base.SpreadElement = base.RestElement = function(node, st, c) { |
|||
c(node.argument, st, "Expression"); |
|||
}; |
|||
base.TryStatement = function(node, st, c) { |
|||
c(node.block, st, "Statement"); |
|||
if (node.handler) c(node.handler.body, st, "ScopeBody"); |
|||
if (node.finalizer) c(node.finalizer, st, "Statement"); |
|||
}; |
|||
base.WhileStatement = function(node, st, c) { |
|||
c(node.test, st, "Expression"); |
|||
c(node.body, st, "Statement"); |
|||
}; |
|||
base.DoWhileStatement = base.WhileStatement; |
|||
base.ForStatement = function(node, st, c) { |
|||
if (node.init) c(node.init, st, "ForInit"); |
|||
if (node.test) c(node.test, st, "Expression"); |
|||
if (node.update) c(node.update, st, "Expression"); |
|||
c(node.body, st, "Statement"); |
|||
}; |
|||
base.ForInStatement = base.ForOfStatement = function(node, st, c) { |
|||
c(node.left, st, "ForInit"); |
|||
c(node.right, st, "Expression"); |
|||
c(node.body, st, "Statement"); |
|||
}; |
|||
base.ForInit = function(node, st, c) { |
|||
if (node.type == "VariableDeclaration") c(node, st); |
|||
else c(node, st, "Expression"); |
|||
}; |
|||
base.DebuggerStatement = ignore; |
|||
|
|||
base.FunctionDeclaration = function(node, st, c) { |
|||
c(node, st, "Function"); |
|||
}; |
|||
base.VariableDeclaration = function(node, st, c) { |
|||
for (var i = 0; i < node.declarations.length; ++i) { |
|||
var decl = node.declarations[i]; |
|||
if (decl.init) c(decl.init, st, "Expression"); |
|||
} |
|||
}; |
|||
|
|||
base.Function = function(node, st, c) { |
|||
c(node.body, st, "ScopeBody"); |
|||
}; |
|||
base.ScopeBody = function(node, st, c) { |
|||
c(node, st, "Statement"); |
|||
}; |
|||
|
|||
base.Expression = skipThrough; |
|||
base.ThisExpression = ignore; |
|||
base.ArrayExpression = base.ArrayPattern = function(node, st, c) { |
|||
for (var i = 0; i < node.elements.length; ++i) { |
|||
var elt = node.elements[i]; |
|||
if (elt) c(elt, st, "Expression"); |
|||
} |
|||
}; |
|||
base.ObjectExpression = base.ObjectPattern = function(node, st, c) { |
|||
for (var i = 0; i < node.properties.length; ++i) |
|||
c(node.properties[i], st); |
|||
}; |
|||
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration; |
|||
base.SequenceExpression = base.TemplateLiteral = function(node, st, c) { |
|||
for (var i = 0; i < node.expressions.length; ++i) |
|||
c(node.expressions[i], st, "Expression"); |
|||
}; |
|||
base.UnaryExpression = base.UpdateExpression = function(node, st, c) { |
|||
c(node.argument, st, "Expression"); |
|||
}; |
|||
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = function(node, st, c) { |
|||
c(node.left, st, "Expression"); |
|||
c(node.right, st, "Expression"); |
|||
}; |
|||
base.ConditionalExpression = function(node, st, c) { |
|||
c(node.test, st, "Expression"); |
|||
c(node.consequent, st, "Expression"); |
|||
c(node.alternate, st, "Expression"); |
|||
}; |
|||
base.NewExpression = base.CallExpression = function(node, st, c) { |
|||
c(node.callee, st, "Expression"); |
|||
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i) |
|||
c(node.arguments[i], st, "Expression"); |
|||
}; |
|||
base.MemberExpression = function(node, st, c) { |
|||
c(node.object, st, "Expression"); |
|||
if (node.computed) c(node.property, st, "Expression"); |
|||
}; |
|||
base.ExportDeclaration = function (node, st, c) { |
|||
c(node.declaration, st); |
|||
}; |
|||
base.ImportDeclaration = function (node, st, c) { |
|||
node.specifiers.forEach(function (specifier) { |
|||
c(specifier, st); |
|||
}); |
|||
}; |
|||
base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore; |
|||
|
|||
base.TaggedTemplateExpression = function(node, st, c) { |
|||
c(node.tag, st, "Expression"); |
|||
c(node.quasi, st); |
|||
}; |
|||
base.ClassDeclaration = base.ClassExpression = function(node, st, c) { |
|||
if (node.superClass) c(node.superClass, st, "Expression"); |
|||
for (var i = 0; i < node.body.body.length; i++) |
|||
c(node.body.body[i], st); |
|||
}; |
|||
base.MethodDefinition = base.Property = function(node, st, c) { |
|||
if (node.computed) c(node.key, st, "Expression"); |
|||
c(node.value, st, "Expression"); |
|||
}; |
|||
base.ComprehensionExpression = function(node, st, c) { |
|||
for (var i = 0; i < node.blocks.length; i++) |
|||
c(node.blocks[i].right, st, "Expression"); |
|||
c(node.body, st, "Expression"); |
|||
}; |
|||
|
|||
// NOTE: the stuff below is deprecated, and will be removed when 1.0 is released
|
|||
|
|||
// A custom walker that keeps track of the scope chain and the
|
|||
// variables defined in it.
|
|||
function makeScope(prev, isCatch) { |
|||
return {vars: Object.create(null), prev: prev, isCatch: isCatch}; |
|||
} |
|||
function normalScope(scope) { |
|||
while (scope.isCatch) scope = scope.prev; |
|||
return scope; |
|||
} |
|||
exports.scopeVisitor = exports.make({ |
|||
Function: function(node, scope, c) { |
|||
var inner = makeScope(scope); |
|||
for (var i = 0; i < node.params.length; ++i) |
|||
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]}; |
|||
if (node.id) { |
|||
var decl = node.type == "FunctionDeclaration"; |
|||
(decl ? normalScope(scope) : inner).vars[node.id.name] = |
|||
{type: decl ? "function" : "function name", node: node.id}; |
|||
} |
|||
c(node.body, inner, "ScopeBody"); |
|||
}, |
|||
TryStatement: function(node, scope, c) { |
|||
c(node.block, scope, "Statement"); |
|||
if (node.handler) { |
|||
var inner = makeScope(scope, true); |
|||
inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param}; |
|||
c(node.handler.body, inner, "ScopeBody"); |
|||
} |
|||
if (node.finalizer) c(node.finalizer, scope, "Statement"); |
|||
}, |
|||
VariableDeclaration: function(node, scope, c) { |
|||
var target = normalScope(scope); |
|||
for (var i = 0; i < node.declarations.length; ++i) { |
|||
var decl = node.declarations[i]; |
|||
target.vars[decl.id.name] = {type: "var", node: decl.id}; |
|||
if (decl.init) c(decl.init, scope, "Expression"); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
}); |
Loading…
Reference in new issue