Add rudimentary JavaScript highlighting to query text view
This commit is contained in:
parent
3a8088033a
commit
6e4cbfdb6d
|
@ -42,6 +42,9 @@
|
||||||
D63CDF4523C970C50012D658 /* DatabaseViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D63CDF4323C970C50012D658 /* DatabaseViewController.xib */; };
|
D63CDF4523C970C50012D658 /* DatabaseViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D63CDF4323C970C50012D658 /* DatabaseViewController.xib */; };
|
||||||
D6A7D096243541A400B46857 /* WindowStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A7D095243541A400B46857 /* WindowStatusView.swift */; };
|
D6A7D096243541A400B46857 /* WindowStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A7D095243541A400B46857 /* WindowStatusView.swift */; };
|
||||||
D6A7D09A243546B500B46857 /* WindowStatusView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A7D099243546B500B46857 /* WindowStatusView.xib */; };
|
D6A7D09A243546B500B46857 /* WindowStatusView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A7D099243546B500B46857 /* WindowStatusView.xib */; };
|
||||||
|
D6A7D0A42435885B00B46857 /* JavaScriptHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A7D0A32435885B00B46857 /* JavaScriptHighlighter.swift */; };
|
||||||
|
D6D13B042436C33D00493D97 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D13B032436C33D00493D97 /* main.swift */; };
|
||||||
|
D6D13B082436C34200493D97 /* JavaScriptHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A7D0A32435885B00B46857 /* JavaScriptHighlighter.swift */; };
|
||||||
D6D4665323CB730C00F13B1B /* MongoEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */; };
|
D6D4665323CB730C00F13B1B /* MongoEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -65,6 +68,15 @@
|
||||||
name = "Embed Frameworks";
|
name = "Embed Frameworks";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D6D13AFF2436C33D00493D97 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = /usr/share/man/man1/;
|
||||||
|
dstSubfolderSpec = 0;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 1;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -97,6 +109,9 @@
|
||||||
D63CDF4323C970C50012D658 /* DatabaseViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DatabaseViewController.xib; sourceTree = "<group>"; };
|
D63CDF4323C970C50012D658 /* DatabaseViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DatabaseViewController.xib; sourceTree = "<group>"; };
|
||||||
D6A7D095243541A400B46857 /* WindowStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowStatusView.swift; sourceTree = "<group>"; };
|
D6A7D095243541A400B46857 /* WindowStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowStatusView.swift; sourceTree = "<group>"; };
|
||||||
D6A7D099243546B500B46857 /* WindowStatusView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WindowStatusView.xib; sourceTree = "<group>"; };
|
D6A7D099243546B500B46857 /* WindowStatusView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WindowStatusView.xib; sourceTree = "<group>"; };
|
||||||
|
D6A7D0A32435885B00B46857 /* JavaScriptHighlighter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScriptHighlighter.swift; sourceTree = "<group>"; };
|
||||||
|
D6D13B012436C33D00493D97 /* jstest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jstest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D6D13B032436C33D00493D97 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MongoEvaluator.swift; sourceTree = "<group>"; };
|
D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MongoEvaluator.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -117,6 +132,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D6D13AFE2436C33D00493D97 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -150,6 +172,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D63CDEBC23C837DC0012D658 /* MongoView */,
|
D63CDEBC23C837DC0012D658 /* MongoView */,
|
||||||
|
D6D13B022436C33D00493D97 /* jstest */,
|
||||||
D63CDEBB23C837DC0012D658 /* Products */,
|
D63CDEBB23C837DC0012D658 /* Products */,
|
||||||
D63CDF1523C837F10012D658 /* Frameworks */,
|
D63CDF1523C837F10012D658 /* Frameworks */,
|
||||||
);
|
);
|
||||||
|
@ -159,6 +182,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D63CDEBA23C837DC0012D658 /* MongoView.app */,
|
D63CDEBA23C837DC0012D658 /* MongoView.app */,
|
||||||
|
D6D13B012436C33D00493D97 /* jstest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -170,6 +194,7 @@
|
||||||
D63CDF3423C838190012D658 /* MongoController.swift */,
|
D63CDF3423C838190012D658 /* MongoController.swift */,
|
||||||
D63CDF3323C838190012D658 /* Node.swift */,
|
D63CDF3323C838190012D658 /* Node.swift */,
|
||||||
D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */,
|
D6D4665223CB730C00F13B1B /* MongoEvaluator.swift */,
|
||||||
|
D6A7D0A22435880700B46857 /* Synax Highlighting */,
|
||||||
D60C863B23CA2DD600C9DB8E /* Windows */,
|
D60C863B23CA2DD600C9DB8E /* Windows */,
|
||||||
D60C863C23CA2DDD00C9DB8E /* View Controllers */,
|
D60C863C23CA2DDD00C9DB8E /* View Controllers */,
|
||||||
D63CDEBF23C837DD0012D658 /* Assets.xcassets */,
|
D63CDEBF23C837DD0012D658 /* Assets.xcassets */,
|
||||||
|
@ -196,6 +221,22 @@
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D6A7D0A22435880700B46857 /* Synax Highlighting */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6A7D0A32435885B00B46857 /* JavaScriptHighlighter.swift */,
|
||||||
|
);
|
||||||
|
path = "Synax Highlighting";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D6D13B022436C33D00493D97 /* jstest */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6D13B032436C33D00493D97 /* main.swift */,
|
||||||
|
);
|
||||||
|
path = jstest;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -217,19 +258,39 @@
|
||||||
productReference = D63CDEBA23C837DC0012D658 /* MongoView.app */;
|
productReference = D63CDEBA23C837DC0012D658 /* MongoView.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
D6D13B002436C33D00493D97 /* jstest */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = D6D13B072436C33D00493D97 /* Build configuration list for PBXNativeTarget "jstest" */;
|
||||||
|
buildPhases = (
|
||||||
|
D6D13AFD2436C33D00493D97 /* Sources */,
|
||||||
|
D6D13AFE2436C33D00493D97 /* Frameworks */,
|
||||||
|
D6D13AFF2436C33D00493D97 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = jstest;
|
||||||
|
productName = jstest;
|
||||||
|
productReference = D6D13B012436C33D00493D97 /* jstest */;
|
||||||
|
productType = "com.apple.product-type.tool";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
D63CDEB223C837DC0012D658 /* Project object */ = {
|
D63CDEB223C837DC0012D658 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1130;
|
LastSwiftUpdateCheck = 1140;
|
||||||
LastUpgradeCheck = 1140;
|
LastUpgradeCheck = 1140;
|
||||||
ORGANIZATIONNAME = Shadowfacts;
|
ORGANIZATIONNAME = Shadowfacts;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
D63CDEB923C837DC0012D658 = {
|
D63CDEB923C837DC0012D658 = {
|
||||||
CreatedOnToolsVersion = 11.3;
|
CreatedOnToolsVersion = 11.3;
|
||||||
};
|
};
|
||||||
|
D6D13B002436C33D00493D97 = {
|
||||||
|
CreatedOnToolsVersion = 11.4;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = D63CDEB523C837DC0012D658 /* Build configuration list for PBXProject "MongoView" */;
|
buildConfigurationList = D63CDEB523C837DC0012D658 /* Build configuration list for PBXProject "MongoView" */;
|
||||||
|
@ -246,6 +307,7 @@
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
D63CDEB923C837DC0012D658 /* MongoView */,
|
D63CDEB923C837DC0012D658 /* MongoView */,
|
||||||
|
D6D13B002436C33D00493D97 /* jstest */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -282,10 +344,20 @@
|
||||||
D63CDF4423C970C50012D658 /* DatabaseViewController.swift in Sources */,
|
D63CDF4423C970C50012D658 /* DatabaseViewController.swift in Sources */,
|
||||||
D63CDF3C23C838470012D658 /* DatabaseWindowController.swift in Sources */,
|
D63CDF3C23C838470012D658 /* DatabaseWindowController.swift in Sources */,
|
||||||
D6A7D096243541A400B46857 /* WindowStatusView.swift in Sources */,
|
D6A7D096243541A400B46857 /* WindowStatusView.swift in Sources */,
|
||||||
|
D6A7D0A42435885B00B46857 /* JavaScriptHighlighter.swift in Sources */,
|
||||||
D63CDF4023C839010012D658 /* QueryViewController.swift in Sources */,
|
D63CDF4023C839010012D658 /* QueryViewController.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
D6D13AFD2436C33D00493D97 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D6D13B082436C34200493D97 /* JavaScriptHighlighter.swift in Sources */,
|
||||||
|
D6D13B042436C33D00493D97 /* main.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
@ -457,6 +529,28 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
D6D13B052436C33D00493D97 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
D6D13B062436C33D00493D97 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
@ -478,6 +572,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
D6D13B072436C33D00493D97 /* Build configuration list for PBXNativeTarget "jstest" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
D6D13B052436C33D00493D97 /* Debug */,
|
||||||
|
D6D13B062436C33D00493D97 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = D63CDEB223C837DC0012D658 /* Project object */;
|
rootObject = D63CDEB223C837DC0012D658 /* Project object */;
|
||||||
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
//
|
||||||
|
// JavaScriptHighlighter.swift
|
||||||
|
// MongoView
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 4/1/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
fileprivate let identifiers: CharacterSet = {
|
||||||
|
var set = CharacterSet.alphanumerics
|
||||||
|
set.insert(charactersIn: "$_")
|
||||||
|
return set
|
||||||
|
}()
|
||||||
|
fileprivate let identifierStarts: CharacterSet = {
|
||||||
|
var set = identifiers
|
||||||
|
set.subtract(.decimalDigits)
|
||||||
|
return set
|
||||||
|
}()
|
||||||
|
fileprivate let operators = CharacterSet(charactersIn: "+-*/<>=")
|
||||||
|
|
||||||
|
class JavaScriptHighlighter {
|
||||||
|
private let text: String
|
||||||
|
private var attributed: NSMutableAttributedString!
|
||||||
|
private var currentIndex: String.Index!
|
||||||
|
private var indent = ""
|
||||||
|
|
||||||
|
init(text: String) {
|
||||||
|
self.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
private func print(_ str: String) {
|
||||||
|
Swift.print("\(indent)\(str)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func range(from: String.Index, to: String.Index) -> NSRange {
|
||||||
|
return NSRange(from..<to, in: text)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func prevCharRange() -> NSRange {
|
||||||
|
return range(from: text.index(before: currentIndex), to: currentIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func peek() -> Unicode.Scalar? {
|
||||||
|
guard currentIndex < text.endIndex else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return text.unicodeScalars[currentIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
private func peek(length: Int) -> Substring {
|
||||||
|
let realLength = min(length, text.distance(from: currentIndex, to: text.endIndex))
|
||||||
|
return text[currentIndex..<text.index(currentIndex, offsetBy: realLength)]
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
private func consume() -> Unicode.Scalar? {
|
||||||
|
let c = peek()
|
||||||
|
currentIndex = text.index(after: currentIndex)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func highlight(mutableAttributed: NSMutableAttributedString? = nil) -> NSAttributedString {
|
||||||
|
attributed = mutableAttributed ?? NSMutableAttributedString(attributedString: NSAttributedString(string: text))
|
||||||
|
|
||||||
|
let fullRange = NSRange(location: 0, length: attributed.length)
|
||||||
|
attributed.setAttributes([
|
||||||
|
.foregroundColor: NSColor.textColor,
|
||||||
|
.font: NSFont.monospacedSystemFont(ofSize: 13, weight: .regular)
|
||||||
|
], range: fullRange)
|
||||||
|
|
||||||
|
currentIndex = text.startIndex
|
||||||
|
while currentIndex < text.endIndex {
|
||||||
|
consumeExpression()
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributed
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeExpression() {
|
||||||
|
guard let char = peek() else { return }
|
||||||
|
|
||||||
|
if identifierStarts.contains(char) {
|
||||||
|
consumeIdentifier()
|
||||||
|
} else if char == "'" || char == "\"" {
|
||||||
|
consumeString()
|
||||||
|
} else if char == "`" {
|
||||||
|
consumeTemplateString()
|
||||||
|
} else if CharacterSet.decimalDigits.contains(char) {
|
||||||
|
consumeNumber()
|
||||||
|
} else if operators.contains(char) {
|
||||||
|
consumeOperator()
|
||||||
|
} else if char == "(" {
|
||||||
|
consumeFunctionCallOrGrouping()
|
||||||
|
} else if char == "{" {
|
||||||
|
consumeObject()
|
||||||
|
} else if char == "[" {
|
||||||
|
consumeArray()
|
||||||
|
} else if char == "." {
|
||||||
|
consumeDotLookup()
|
||||||
|
} else {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeIdentifier() {
|
||||||
|
let identifierStart = currentIndex!
|
||||||
|
while let char = peek(), identifiers.contains(char) {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
print("Identifier: '\(text[identifierStart..<currentIndex])'")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeNumber() {
|
||||||
|
let numberStart = currentIndex!
|
||||||
|
while let char = peek(), CharacterSet.decimalDigits.contains(char) {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
if currentIndex < text.endIndex && peek() == "." {
|
||||||
|
consume()
|
||||||
|
while let char = peek(), CharacterSet.decimalDigits.contains(char) {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("Number: \(text[numberStart..<currentIndex])")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemBlue, range: range(from: numberStart, to: currentIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeString() {
|
||||||
|
let stringStart = currentIndex!
|
||||||
|
let startChar = consume()
|
||||||
|
while currentIndex < text.endIndex && peek() != startChar && (currentIndex == text.startIndex || text[text.index(before: currentIndex)] != "\\") {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
if currentIndex < text.endIndex {
|
||||||
|
consume() // string closing quote
|
||||||
|
}
|
||||||
|
print("String: \(text[stringStart..<currentIndex])")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringStart, to: currentIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeTemplateString() {
|
||||||
|
var stringFragmentStart: String.Index? = currentIndex
|
||||||
|
consume() // opening `
|
||||||
|
while currentIndex < text.endIndex {
|
||||||
|
if peek(length: 2) == "${" {
|
||||||
|
consume() // $
|
||||||
|
consume() // {
|
||||||
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringFragmentStart!, to: currentIndex))
|
||||||
|
consumeTemplateStringExpression()
|
||||||
|
stringFragmentStart = currentIndex
|
||||||
|
if currentIndex < text.endIndex && peek() == "}" {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
} else if peek() == "`" {
|
||||||
|
stringFragmentStart = stringFragmentStart ?? currentIndex
|
||||||
|
consume() // `
|
||||||
|
print("Template string fragment: '\(text[stringFragmentStart!..<currentIndex])'")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: stringFragmentStart!, to: currentIndex))
|
||||||
|
stringFragmentStart = nil
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let start = stringFragmentStart {
|
||||||
|
print("Template string fragment: '\(text[start..<currentIndex])'")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemRed, range: range(from: start, to: currentIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeTemplateStringExpression() {
|
||||||
|
indent += " "
|
||||||
|
while currentIndex < text.endIndex && peek() != "}" {
|
||||||
|
consumeExpression()
|
||||||
|
}
|
||||||
|
indent = String(indent.dropLast(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeOperator() {
|
||||||
|
print("Operator: \(peek()!)")
|
||||||
|
consume()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeFunctionCallOrGrouping() {
|
||||||
|
consume() // (
|
||||||
|
print("Opening (")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
indent += " "
|
||||||
|
while currentIndex < text.endIndex && peek() != ")" {
|
||||||
|
consumeExpression()
|
||||||
|
}
|
||||||
|
indent = String(indent.dropLast(2))
|
||||||
|
if currentIndex < text.endIndex {
|
||||||
|
consume() // )
|
||||||
|
print("Closing )")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeObject() {
|
||||||
|
consume() // {
|
||||||
|
print("Opening {")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
indent += " "
|
||||||
|
object:
|
||||||
|
while currentIndex < text.endIndex && peek() != "}" {
|
||||||
|
consumeObjectKey()
|
||||||
|
if peek() == ":" {
|
||||||
|
consume() // :
|
||||||
|
while currentIndex < text.endIndex && peek() != "," && peek() != "}" {
|
||||||
|
consumeExpression()
|
||||||
|
}
|
||||||
|
if currentIndex < text.endIndex {
|
||||||
|
break object
|
||||||
|
}
|
||||||
|
} else if peek() == "," {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent = String(indent.dropLast(2))
|
||||||
|
if currentIndex < text.endIndex {
|
||||||
|
consume() // }
|
||||||
|
print("Closing }")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeObjectKey() {
|
||||||
|
guard let char = peek() else { return }
|
||||||
|
let keyStart = currentIndex!
|
||||||
|
if identifierStarts.contains(char) {
|
||||||
|
consumeIdentifier()
|
||||||
|
} else if char == "'" || char == "\"" {
|
||||||
|
consumeString()
|
||||||
|
}
|
||||||
|
print("Object key: '\(text[keyStart..<currentIndex])'")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeDotLookup() {
|
||||||
|
consume() // .
|
||||||
|
print("Dot lookup")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consumeArray() {
|
||||||
|
consume() // [
|
||||||
|
print("Opening [")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
indent += " "
|
||||||
|
array:
|
||||||
|
while currentIndex < text.endIndex && peek() != "]" {
|
||||||
|
print("Array element")
|
||||||
|
while currentIndex < text.endIndex {
|
||||||
|
if peek() == "," {
|
||||||
|
consume() // ,
|
||||||
|
break
|
||||||
|
} else if peek() == "]" {
|
||||||
|
break array
|
||||||
|
} else {
|
||||||
|
indent += " "
|
||||||
|
consumeExpression()
|
||||||
|
indent = String(indent.dropLast(2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent = String(indent.dropLast(2))
|
||||||
|
if currentIndex < text.endIndex {
|
||||||
|
consume() // ]
|
||||||
|
print("Closing ]")
|
||||||
|
attributed.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: prevCharRange())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -53,6 +53,8 @@ class QueryViewController: NSViewController {
|
||||||
queryTextView.font = .monospacedSystemFont(ofSize: 13, weight: .regular)
|
queryTextView.font = .monospacedSystemFont(ofSize: 13, weight: .regular)
|
||||||
queryTextView.isAutomaticQuoteSubstitutionEnabled = false
|
queryTextView.isAutomaticQuoteSubstitutionEnabled = false
|
||||||
queryTextView.string = defaultQuery
|
queryTextView.string = defaultQuery
|
||||||
|
queryTextView.delegate = self
|
||||||
|
highlightQuery()
|
||||||
|
|
||||||
outlineView.dataSource = self
|
outlineView.dataSource = self
|
||||||
outlineView.delegate = self
|
outlineView.delegate = self
|
||||||
|
@ -76,6 +78,10 @@ class QueryViewController: NSViewController {
|
||||||
view.window!.makeFirstResponder(outlineView)
|
view.window!.makeFirstResponder(outlineView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func highlightQuery() {
|
||||||
|
_ = JavaScriptHighlighter(text: queryTextView.string).highlight(mutableAttributed: queryTextView.textStorage!)
|
||||||
|
}
|
||||||
|
|
||||||
func refresh(reload: Bool = true) {
|
func refresh(reload: Bool = true) {
|
||||||
if let query = mostRecentQuery {
|
if let query = mostRecentQuery {
|
||||||
let connStr = "\(mongoController.connectionString)/\(collection.database)"
|
let connStr = "\(mongoController.connectionString)/\(collection.database)"
|
||||||
|
@ -170,6 +176,12 @@ class QueryViewController: NSViewController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension QueryViewController: NSTextViewDelegate {
|
||||||
|
func textDidChange(_ notification: Notification) {
|
||||||
|
highlightQuery()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension QueryViewController: NSMenuItemValidation {
|
extension QueryViewController: NSMenuItemValidation {
|
||||||
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
|
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
|
||||||
if menuItem.action == #selector(deleteNode(_:)) {
|
if menuItem.action == #selector(deleteNode(_:)) {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
//
|
||||||
|
// main.swift
|
||||||
|
// jstest
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 4/2/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let source = "`+`${{a:3}}`"
|
||||||
|
_ = JavaScriptHighlighter(text: source).highlight()
|
||||||
|
|
Loading…
Reference in New Issue