sqlui 0.1.38 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/app/args.rb +11 -3
- data/app/database_config.rb +12 -1
- data/app/server.rb +1 -0
- data/app/sqlui_config.rb +1 -0
- data/client/resources/sqlui.js +234 -94
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473ef1be7a3ad0a4e28d2ebf7ab4f52bfcb140a24ccbfcabab5ccbccf7e5d699
|
4
|
+
data.tar.gz: 8236b7ddf4245a64738b304029a291a04c33d7079045b3456d61b22d1f1fcf1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38dd32e339981bba7a401a6c53f5cf8233ffbe8d98fc69f79ceb4c575adbe8b7a2d7e6c7ab97f9a835ae69f63423b8b925978565c5d6e66aa5a3efa71e937e5a
|
7
|
+
data.tar.gz: 4e56ef90eba0ce8ad231fb80f7c93cb740eab46208a0c8a27d16e3f34568f4b40ece0c68d92f0c81101ad784e78609f34c90158731ef61d72ee3952847c6a2e1
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.39
|
data/app/args.rb
CHANGED
@@ -20,13 +20,21 @@ class Args
|
|
20
20
|
value
|
21
21
|
end
|
22
22
|
|
23
|
+
def self.fetch_optional_hash(hash, key)
|
24
|
+
fetch_optional(hash, key, Hash)
|
25
|
+
end
|
26
|
+
|
23
27
|
def self.fetch_non_nil(hash, key, *classes)
|
24
28
|
raise ArgumentError, "required parameter #{key} missing" unless hash.key?(key)
|
25
29
|
|
26
|
-
|
27
|
-
raise ArgumentError, "required parameter #{key} null" if value.nil?
|
30
|
+
raise ArgumentError, "required parameter #{key} null" if hash[key].nil?
|
28
31
|
|
29
|
-
|
32
|
+
fetch_optional(hash, key, *classes)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.fetch_optional(hash, key, *classes)
|
36
|
+
value = hash[key]
|
37
|
+
if value && classes.size.positive? && !classes.find { |clazz| value.is_a?(clazz) }
|
30
38
|
if classes.size != 1
|
31
39
|
raise ArgumentError, "required parameter #{key} not #{classes.map(&:to_s).map(&:downcase).join(' or ')}"
|
32
40
|
end
|
data/app/database_config.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'mysql2'
|
4
|
+
require 'set'
|
5
|
+
|
4
6
|
require_relative 'args'
|
5
7
|
|
6
8
|
# Config for a single database.
|
7
9
|
class DatabaseConfig
|
8
|
-
attr_reader :display_name, :description, :url_path, :saved_path, :client_params
|
10
|
+
attr_reader :display_name, :description, :url_path, :saved_path, :table_aliases, :client_params
|
9
11
|
|
10
12
|
def initialize(hash)
|
11
13
|
@display_name = Args.fetch_non_empty_string(hash, :display_name).strip
|
@@ -15,6 +17,15 @@ class DatabaseConfig
|
|
15
17
|
raise ArgumentError, 'url_path should not end with a /' if @url_path.length > 1 && @url_path.end_with?('/')
|
16
18
|
|
17
19
|
@saved_path = Args.fetch_non_empty_string(hash, :saved_path).strip
|
20
|
+
@table_aliases = Args.fetch_optional_hash(hash, :table_aliases) || {}
|
21
|
+
@table_aliases = @table_aliases.each do |table, a|
|
22
|
+
raise ArgumentError, "invalid alias for table #{table} (#{a}), expected string" unless a.is_a?(String)
|
23
|
+
end
|
24
|
+
duplicate_aliases = @table_aliases.reject { |(_, v)| @table_aliases.values.count(v) == 1 }.to_h.values.to_set
|
25
|
+
if @table_aliases.values.to_set.size < @table_aliases.values.size
|
26
|
+
raise ArgumentError, "duplicate table aliases: #{duplicate_aliases.join(', ')}"
|
27
|
+
end
|
28
|
+
|
18
29
|
@client_params = Args.fetch_non_empty_hash(hash, :client_params)
|
19
30
|
end
|
20
31
|
|
data/app/server.rb
CHANGED
@@ -57,6 +57,7 @@ class Server < Sinatra::Base
|
|
57
57
|
server: "#{config.name} - #{database.display_name}",
|
58
58
|
list_url_path: config.list_url_path,
|
59
59
|
schemas: DatabaseMetadata.lookup(client, database),
|
60
|
+
table_aliases: database.table_aliases,
|
60
61
|
saved: Dir.glob("#{database.saved_path}/*.sql").to_h do |path|
|
61
62
|
contents = File.read(path)
|
62
63
|
comment_lines = contents.split("\n").take_while do |l|
|
data/app/sqlui_config.rb
CHANGED
data/client/resources/sqlui.js
CHANGED
@@ -1662,18 +1662,20 @@
|
|
1662
1662
|
return 0;
|
1663
1663
|
},
|
1664
1664
|
reconfigure: (state, oldState) => {
|
1665
|
-
let newVal =
|
1666
|
-
let oldAddr = oldState.config.address[id];
|
1665
|
+
let newVal, oldAddr = oldState.config.address[id];
|
1667
1666
|
if (oldAddr != null) {
|
1668
1667
|
let oldVal = getAddr(oldState, oldAddr);
|
1669
1668
|
if (this.dependencies.every(dep => {
|
1670
1669
|
return dep instanceof Facet ? oldState.facet(dep) === state.facet(dep) :
|
1671
1670
|
dep instanceof StateField ? oldState.field(dep, false) == state.field(dep, false) : true;
|
1672
|
-
}) || (multi ? compareArray(newVal, oldVal, compare) : compare(newVal, oldVal))) {
|
1671
|
+
}) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) {
|
1673
1672
|
state.values[idx] = oldVal;
|
1674
1673
|
return 0;
|
1675
1674
|
}
|
1676
1675
|
}
|
1676
|
+
else {
|
1677
|
+
newVal = getter(state);
|
1678
|
+
}
|
1677
1679
|
state.values[idx] = newVal;
|
1678
1680
|
return 1 /* SlotStatus.Changed */;
|
1679
1681
|
}
|
@@ -2807,6 +2809,18 @@
|
|
2807
2809
|
/**
|
2808
2810
|
Find the values for a given language data field, provided by the
|
2809
2811
|
the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet.
|
2812
|
+
|
2813
|
+
Examples of language data fields are...
|
2814
|
+
|
2815
|
+
- [`"commentTokens"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying
|
2816
|
+
comment syntax.
|
2817
|
+
- [`"autocomplete"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override)
|
2818
|
+
for providing language-specific completion sources.
|
2819
|
+
- [`"wordChars"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding
|
2820
|
+
characters that should be considered part of words in this
|
2821
|
+
language.
|
2822
|
+
- [`"closeBrackets"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls
|
2823
|
+
bracket closing behavior.
|
2810
2824
|
*/
|
2811
2825
|
languageDataAt(name, pos, side = -1) {
|
2812
2826
|
let values = [];
|
@@ -3311,7 +3325,7 @@
|
|
3311
3325
|
*/
|
3312
3326
|
static eq(oldSets, newSets, from = 0, to) {
|
3313
3327
|
if (to == null)
|
3314
|
-
to = 1000000000 /* C.Far
|
3328
|
+
to = 1000000000 /* C.Far */ - 1;
|
3315
3329
|
let a = oldSets.filter(set => !set.isEmpty && newSets.indexOf(set) < 0);
|
3316
3330
|
let b = newSets.filter(set => !set.isEmpty && oldSets.indexOf(set) < 0);
|
3317
3331
|
if (a.length != b.length)
|
@@ -5019,7 +5033,7 @@
|
|
5019
5033
|
if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
|
5020
5034
|
break;
|
5021
5035
|
}
|
5022
|
-
return flattenRect(rect, this.side > 0);
|
5036
|
+
return this.length ? rect : flattenRect(rect, this.side > 0);
|
5023
5037
|
}
|
5024
5038
|
get isEditable() { return false; }
|
5025
5039
|
destroy() {
|
@@ -5909,6 +5923,9 @@
|
|
5909
5923
|
const perLineTextDirection = /*@__PURE__*/Facet.define({
|
5910
5924
|
combine: values => values.some(x => x)
|
5911
5925
|
});
|
5926
|
+
const nativeSelectionHidden = /*@__PURE__*/Facet.define({
|
5927
|
+
combine: values => values.some(x => x)
|
5928
|
+
});
|
5912
5929
|
class ScrollTarget {
|
5913
5930
|
constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
|
5914
5931
|
this.range = range;
|
@@ -6821,8 +6838,9 @@
|
|
6821
6838
|
enforceCursorAssoc() {
|
6822
6839
|
if (this.compositionDeco.size)
|
6823
6840
|
return;
|
6824
|
-
let cursor =
|
6825
|
-
let sel = getSelection$1(
|
6841
|
+
let { view } = this, cursor = view.state.selection.main;
|
6842
|
+
let sel = getSelection$1(view.root);
|
6843
|
+
let { anchorNode, anchorOffset } = view.observer.selectionRange;
|
6826
6844
|
if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
|
6827
6845
|
return;
|
6828
6846
|
let line = LineView.find(this, cursor.head);
|
@@ -6837,6 +6855,12 @@
|
|
6837
6855
|
let dom = this.domAtPos(cursor.head + cursor.assoc);
|
6838
6856
|
sel.collapse(dom.node, dom.offset);
|
6839
6857
|
sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
|
6858
|
+
// This can go wrong in corner cases like single-character lines,
|
6859
|
+
// so check and reset if necessary.
|
6860
|
+
view.observer.readSelectionRange();
|
6861
|
+
let newRange = view.observer.selectionRange;
|
6862
|
+
if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
|
6863
|
+
sel.collapse(anchorNode, anchorOffset);
|
6840
6864
|
}
|
6841
6865
|
mayControlSelection() {
|
6842
6866
|
let active = this.view.root.activeElement;
|
@@ -8170,9 +8194,9 @@
|
|
8170
8194
|
|
8171
8195
|
const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"];
|
8172
8196
|
class HeightOracle {
|
8173
|
-
constructor() {
|
8197
|
+
constructor(lineWrapping) {
|
8198
|
+
this.lineWrapping = lineWrapping;
|
8174
8199
|
this.doc = Text.empty;
|
8175
|
-
this.lineWrapping = false;
|
8176
8200
|
this.heightSamples = {};
|
8177
8201
|
this.lineHeight = 14;
|
8178
8202
|
this.charWidth = 7;
|
@@ -8911,7 +8935,6 @@
|
|
8911
8935
|
this.contentDOMHeight = 0;
|
8912
8936
|
this.editorHeight = 0;
|
8913
8937
|
this.editorWidth = 0;
|
8914
|
-
this.heightOracle = new HeightOracle;
|
8915
8938
|
// See VP.MaxDOMHeight
|
8916
8939
|
this.scaler = IdScaler;
|
8917
8940
|
this.scrollTarget = null;
|
@@ -8920,7 +8943,7 @@
|
|
8920
8943
|
// Flag set when editor content was redrawn, so that the next
|
8921
8944
|
// measure stage knows it must read DOM layout
|
8922
8945
|
this.mustMeasureContent = true;
|
8923
|
-
this.defaultTextDirection = Direction.
|
8946
|
+
this.defaultTextDirection = Direction.LTR;
|
8924
8947
|
this.visibleRanges = [];
|
8925
8948
|
// Cursor 'assoc' is only significant when the cursor is on a line
|
8926
8949
|
// wrap point, where it must stick to the character that it is
|
@@ -8931,6 +8954,8 @@
|
|
8931
8954
|
// boundary and, if so, reset it to make sure it is positioned in
|
8932
8955
|
// the right place.
|
8933
8956
|
this.mustEnforceCursorAssoc = false;
|
8957
|
+
let guessWrapping = state.facet(contentAttributes).some(v => typeof v != "function" && v.class == "cm-lineWrapping");
|
8958
|
+
this.heightOracle = new HeightOracle(guessWrapping);
|
8934
8959
|
this.stateDeco = state.facet(decorations).filter(d => typeof d != "function");
|
8935
8960
|
this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
|
8936
8961
|
this.viewport = this.getViewport(0, null);
|
@@ -8985,7 +9010,8 @@
|
|
8985
9010
|
if (scrollTarget)
|
8986
9011
|
this.scrollTarget = scrollTarget;
|
8987
9012
|
if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
|
8988
|
-
update.state.selection.main.empty && update.state.selection.main.assoc
|
9013
|
+
update.state.selection.main.empty && update.state.selection.main.assoc &&
|
9014
|
+
!update.state.facet(nativeSelectionHidden))
|
8989
9015
|
this.mustEnforceCursorAssoc = true;
|
8990
9016
|
}
|
8991
9017
|
measure(view) {
|
@@ -9048,7 +9074,7 @@
|
|
9048
9074
|
oracle.heightChanged = false;
|
9049
9075
|
for (let vp of this.viewports) {
|
9050
9076
|
let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp);
|
9051
|
-
this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights));
|
9077
|
+
this.heightMap = (refresh ? HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle, [new ChangedRange(0, 0, 0, view.state.doc.length)]) : this.heightMap).updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights));
|
9052
9078
|
}
|
9053
9079
|
if (oracle.heightChanged)
|
9054
9080
|
result |= 2 /* UpdateFlag.Height */;
|
@@ -9614,7 +9640,11 @@
|
|
9614
9640
|
this.bounds = null;
|
9615
9641
|
this.text = "";
|
9616
9642
|
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
9617
|
-
if (
|
9643
|
+
if (view.state.readOnly && start > -1) {
|
9644
|
+
// Ignore changes when the editor is read-only
|
9645
|
+
this.newSel = null;
|
9646
|
+
}
|
9647
|
+
else if (start > -1 && (this.bounds = view.docView.domBoundsAround(start, end, 0))) {
|
9618
9648
|
let selPoints = iHead || iAnchor ? [] : selectionPoints(view);
|
9619
9649
|
let reader = new DOMReader(selPoints, view.state);
|
9620
9650
|
reader.readRange(this.bounds.startDOM, this.bounds.endDOM);
|
@@ -9908,7 +9938,8 @@
|
|
9908
9938
|
this.onScroll = this.onScroll.bind(this);
|
9909
9939
|
if (typeof ResizeObserver == "function") {
|
9910
9940
|
this.resize = new ResizeObserver(() => {
|
9911
|
-
|
9941
|
+
var _a;
|
9942
|
+
if (((_a = this.view.docView) === null || _a === void 0 ? void 0 : _a.lastUpdate) < Date.now() - 75)
|
9912
9943
|
this.onResize();
|
9913
9944
|
});
|
9914
9945
|
this.resize.observe(view.scrollDOM);
|
@@ -11400,7 +11431,7 @@
|
|
11400
11431
|
if (scopeObj) {
|
11401
11432
|
if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
|
11402
11433
|
return true;
|
11403
|
-
if (isChar && (event.
|
11434
|
+
if (isChar && (event.altKey || event.metaKey || event.ctrlKey) &&
|
11404
11435
|
(baseName = base[event.keyCode]) && baseName != name) {
|
11405
11436
|
if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
|
11406
11437
|
return true;
|
@@ -11452,7 +11483,8 @@
|
|
11452
11483
|
return [
|
11453
11484
|
selectionConfig.of(config),
|
11454
11485
|
drawSelectionPlugin,
|
11455
|
-
hideNativeSelection
|
11486
|
+
hideNativeSelection,
|
11487
|
+
nativeSelectionHidden.of(true)
|
11456
11488
|
];
|
11457
11489
|
}
|
11458
11490
|
class Piece {
|
@@ -12210,6 +12242,9 @@
|
|
12210
12242
|
keyup(e) {
|
12211
12243
|
if (e.keyCode == code || !getter(e))
|
12212
12244
|
this.set(false);
|
12245
|
+
},
|
12246
|
+
mousemove(e) {
|
12247
|
+
this.set(getter(e));
|
12213
12248
|
}
|
12214
12249
|
}
|
12215
12250
|
});
|
@@ -12444,7 +12479,7 @@
|
|
12444
12479
|
dom.classList.toggle("cm-tooltip-above", above);
|
12445
12480
|
dom.classList.toggle("cm-tooltip-below", !above);
|
12446
12481
|
if (tView.positioned)
|
12447
|
-
tView.positioned();
|
12482
|
+
tView.positioned(measured.space);
|
12448
12483
|
}
|
12449
12484
|
}
|
12450
12485
|
maybeMeasure() {
|
@@ -12560,10 +12595,10 @@
|
|
12560
12595
|
}
|
12561
12596
|
this.mounted = true;
|
12562
12597
|
}
|
12563
|
-
positioned() {
|
12598
|
+
positioned(space) {
|
12564
12599
|
for (let hostedView of this.manager.tooltipViews) {
|
12565
12600
|
if (hostedView.positioned)
|
12566
|
-
hostedView.positioned();
|
12601
|
+
hostedView.positioned(space);
|
12567
12602
|
}
|
12568
12603
|
}
|
12569
12604
|
update(update) {
|
@@ -12660,10 +12695,10 @@
|
|
12660
12695
|
}
|
12661
12696
|
}
|
12662
12697
|
}
|
12663
|
-
mouseleave() {
|
12698
|
+
mouseleave(e) {
|
12664
12699
|
clearTimeout(this.hoverTimeout);
|
12665
12700
|
this.hoverTimeout = -1;
|
12666
|
-
if (this.active)
|
12701
|
+
if (this.active && !isInTooltip(e.relatedTarget))
|
12667
12702
|
this.view.dispatch({ effects: this.setHover.of(null) });
|
12668
12703
|
}
|
12669
12704
|
destroy() {
|
@@ -15988,8 +16023,10 @@
|
|
15988
16023
|
Facet that defines a way to provide a function that computes the
|
15989
16024
|
appropriate indentation depth, as a column number (see
|
15990
16025
|
[`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)), at the start of a given
|
15991
|
-
line
|
15992
|
-
determined
|
16026
|
+
line. A return value of `null` indicates no indentation can be
|
16027
|
+
determined, and the line should inherit the indentation of the one
|
16028
|
+
above it. A return value of `undefined` defers to the next indent
|
16029
|
+
service.
|
15993
16030
|
*/
|
15994
16031
|
const indentService = /*@__PURE__*/Facet.define();
|
15995
16032
|
/**
|
@@ -16047,7 +16084,7 @@
|
|
16047
16084
|
context = new IndentContext(context);
|
16048
16085
|
for (let service of context.state.facet(indentService)) {
|
16049
16086
|
let result = service(context, pos);
|
16050
|
-
if (result
|
16087
|
+
if (result !== undefined)
|
16051
16088
|
return result;
|
16052
16089
|
}
|
16053
16090
|
let tree = syntaxTree(context.state);
|
@@ -16355,7 +16392,7 @@
|
|
16355
16392
|
let tree = syntaxTree(state);
|
16356
16393
|
if (tree.length < end)
|
16357
16394
|
return null;
|
16358
|
-
let inner = tree.resolveInner(end);
|
16395
|
+
let inner = tree.resolveInner(end, 1);
|
16359
16396
|
let found = null;
|
16360
16397
|
for (let cur = inner; cur; cur = cur.parent) {
|
16361
16398
|
if (cur.to <= end || cur.from > end)
|
@@ -19520,14 +19557,14 @@
|
|
19520
19557
|
crelt("br"),
|
19521
19558
|
this.replaceField,
|
19522
19559
|
button("replace", () => replaceNext(view), [phrase(view, "replace")]),
|
19523
|
-
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
|
19524
|
-
|
19525
|
-
|
19526
|
-
|
19527
|
-
|
19528
|
-
|
19529
|
-
|
19530
|
-
]
|
19560
|
+
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
|
19561
|
+
],
|
19562
|
+
crelt("button", {
|
19563
|
+
name: "close",
|
19564
|
+
onclick: () => closeSearchPanel(view),
|
19565
|
+
"aria-label": phrase(view, "close"),
|
19566
|
+
type: "button"
|
19567
|
+
}, ["×"])
|
19531
19568
|
]);
|
19532
19569
|
}
|
19533
19570
|
commit() {
|
@@ -19767,6 +19804,11 @@
|
|
19767
19804
|
return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
|
19768
19805
|
}
|
19769
19806
|
/**
|
19807
|
+
This annotation is added to transactions that are produced by
|
19808
|
+
picking a completion.
|
19809
|
+
*/
|
19810
|
+
const pickedCompletion = /*@__PURE__*/Annotation.define();
|
19811
|
+
/**
|
19770
19812
|
Helper function that returns a transaction spec which inserts a
|
19771
19813
|
completion's text in the main selection range, and any other
|
19772
19814
|
selection range that has the same text in front of it.
|
@@ -19792,7 +19834,7 @@
|
|
19792
19834
|
const apply = option.completion.apply || option.completion.label;
|
19793
19835
|
let result = option.source;
|
19794
19836
|
if (typeof apply == "string")
|
19795
|
-
view.dispatch(insertCompletionText(view.state, apply, result.from, result.to));
|
19837
|
+
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
19796
19838
|
else
|
19797
19839
|
apply(view, option.completion, result.from, result.to);
|
19798
19840
|
}
|
@@ -20027,6 +20069,7 @@
|
|
20027
20069
|
write: (pos) => this.positionInfo(pos),
|
20028
20070
|
key: this
|
20029
20071
|
};
|
20072
|
+
this.space = null;
|
20030
20073
|
let cState = view.state.field(stateField);
|
20031
20074
|
let { options, selected } = cState.open;
|
20032
20075
|
let config = view.state.facet(completionConfig);
|
@@ -20052,10 +20095,17 @@
|
|
20052
20095
|
}
|
20053
20096
|
mount() { this.updateSel(); }
|
20054
20097
|
update(update) {
|
20055
|
-
|
20098
|
+
var _a, _b, _c;
|
20099
|
+
let cState = update.state.field(this.stateField);
|
20100
|
+
let prevState = update.startState.field(this.stateField);
|
20101
|
+
if (cState != prevState) {
|
20056
20102
|
this.updateSel();
|
20103
|
+
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
20104
|
+
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
20105
|
+
}
|
20057
20106
|
}
|
20058
|
-
positioned() {
|
20107
|
+
positioned(space) {
|
20108
|
+
this.space = space;
|
20059
20109
|
if (this.info)
|
20060
20110
|
this.view.requestMeasure(this.placeInfo);
|
20061
20111
|
}
|
@@ -20122,27 +20172,32 @@
|
|
20122
20172
|
let sel = this.dom.querySelector("[aria-selected]");
|
20123
20173
|
if (!sel || !this.info)
|
20124
20174
|
return null;
|
20125
|
-
let win = this.dom.ownerDocument.defaultView || window;
|
20126
20175
|
let listRect = this.dom.getBoundingClientRect();
|
20127
20176
|
let infoRect = this.info.getBoundingClientRect();
|
20128
20177
|
let selRect = sel.getBoundingClientRect();
|
20129
|
-
|
20178
|
+
let space = this.space;
|
20179
|
+
if (!space) {
|
20180
|
+
let win = this.dom.ownerDocument.defaultView || window;
|
20181
|
+
space = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight };
|
20182
|
+
}
|
20183
|
+
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
|
20184
|
+
selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
20130
20185
|
return null;
|
20131
20186
|
let rtl = this.view.textDirection == Direction.RTL, left = rtl, narrow = false, maxWidth;
|
20132
20187
|
let top = "", bottom = "";
|
20133
|
-
let spaceLeft = listRect.left, spaceRight =
|
20188
|
+
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
|
20134
20189
|
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
20135
20190
|
left = false;
|
20136
20191
|
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
20137
20192
|
left = true;
|
20138
20193
|
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
|
20139
|
-
top = (Math.max(
|
20194
|
+
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
|
20140
20195
|
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
|
20141
20196
|
}
|
20142
20197
|
else {
|
20143
20198
|
narrow = true;
|
20144
|
-
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right :
|
20145
|
-
let spaceBelow =
|
20199
|
+
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
|
20200
|
+
let spaceBelow = space.bottom - listRect.bottom;
|
20146
20201
|
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
|
20147
20202
|
top = (selRect.bottom - listRect.top) + "px";
|
20148
20203
|
else // Above it
|
@@ -20251,21 +20306,24 @@
|
|
20251
20306
|
return result;
|
20252
20307
|
}
|
20253
20308
|
class CompletionDialog {
|
20254
|
-
constructor(options, attrs, tooltip, timestamp, selected) {
|
20309
|
+
constructor(options, attrs, tooltip, timestamp, selected, disabled) {
|
20255
20310
|
this.options = options;
|
20256
20311
|
this.attrs = attrs;
|
20257
20312
|
this.tooltip = tooltip;
|
20258
20313
|
this.timestamp = timestamp;
|
20259
20314
|
this.selected = selected;
|
20315
|
+
this.disabled = disabled;
|
20260
20316
|
}
|
20261
20317
|
setSelected(selected, id) {
|
20262
20318
|
return selected == this.selected || selected >= this.options.length ? this
|
20263
|
-
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
|
20319
|
+
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
|
20264
20320
|
}
|
20265
20321
|
static build(active, state, id, prev, conf) {
|
20266
20322
|
let options = sortOptions(active, state);
|
20267
|
-
if (!options.length)
|
20268
|
-
return
|
20323
|
+
if (!options.length) {
|
20324
|
+
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
|
20325
|
+
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
|
20326
|
+
}
|
20269
20327
|
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
|
20270
20328
|
if (prev && prev.selected != selected && prev.selected != -1) {
|
20271
20329
|
let selectedValue = prev.options[prev.selected].completion;
|
@@ -20279,10 +20337,10 @@
|
|
20279
20337
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
20280
20338
|
create: completionTooltip(completionState),
|
20281
20339
|
above: conf.aboveCursor,
|
20282
|
-
}, prev ? prev.timestamp : Date.now(), selected);
|
20340
|
+
}, prev ? prev.timestamp : Date.now(), selected, false);
|
20283
20341
|
}
|
20284
20342
|
map(changes) {
|
20285
|
-
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected);
|
20343
|
+
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
|
20286
20344
|
}
|
20287
20345
|
}
|
20288
20346
|
class CompletionState {
|
@@ -20305,9 +20363,12 @@
|
|
20305
20363
|
});
|
20306
20364
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
20307
20365
|
active = this.active;
|
20308
|
-
let open =
|
20309
|
-
|
20310
|
-
|
20366
|
+
let open = this.open;
|
20367
|
+
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
20368
|
+
!sameResults(active, this.active))
|
20369
|
+
open = CompletionDialog.build(active, state, this.id, this.open, conf);
|
20370
|
+
else if (open && tr.docChanged)
|
20371
|
+
open = open.map(tr.changes);
|
20311
20372
|
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
|
20312
20373
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
|
20313
20374
|
for (let effect of tr.effects)
|
@@ -20447,7 +20508,7 @@
|
|
20447
20508
|
function moveCompletionSelection(forward, by = "option") {
|
20448
20509
|
return (view) => {
|
20449
20510
|
let cState = view.state.field(completionState, false);
|
20450
|
-
if (!cState || !cState.open ||
|
20511
|
+
if (!cState || !cState.open || cState.open.disabled ||
|
20451
20512
|
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
20452
20513
|
return false;
|
20453
20514
|
let step = 1, tooltip;
|
@@ -20472,7 +20533,8 @@
|
|
20472
20533
|
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
20473
20534
|
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
20474
20535
|
return false;
|
20475
|
-
|
20536
|
+
if (!cState.open.disabled)
|
20537
|
+
applyCompletion(view, cState.open.options[cState.open.selected]);
|
20476
20538
|
return true;
|
20477
20539
|
};
|
20478
20540
|
/**
|
@@ -20677,10 +20739,16 @@
|
|
20677
20739
|
background: "#17c",
|
20678
20740
|
color: "white",
|
20679
20741
|
},
|
20742
|
+
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
20743
|
+
background: "#777",
|
20744
|
+
},
|
20680
20745
|
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
20681
20746
|
background: "#347",
|
20682
20747
|
color: "white",
|
20683
20748
|
},
|
20749
|
+
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
20750
|
+
background: "#444",
|
20751
|
+
},
|
20684
20752
|
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
20685
20753
|
content: '"···"',
|
20686
20754
|
opacity: 0.5,
|
@@ -22355,7 +22423,7 @@
|
|
22355
22423
|
}
|
22356
22424
|
let next = input.next, low = 0, high = data[state + 2];
|
22357
22425
|
// Special case for EOF
|
22358
|
-
if (input.next < 0 && high > low && data[accEnd + high * 3 - 3] == 65535 /* End */) {
|
22426
|
+
if (input.next < 0 && high > low && data[accEnd + high * 3 - 3] == 65535 /* End */ && data[accEnd + high * 3 - 3] == 65535 /* End */) {
|
22359
22427
|
state = data[accEnd + high * 3 - 1];
|
22360
22428
|
continue scan;
|
22361
22429
|
}
|
@@ -22363,7 +22431,7 @@
|
|
22363
22431
|
for (; low < high;) {
|
22364
22432
|
let mid = (low + high) >> 1;
|
22365
22433
|
let index = accEnd + mid + (mid << 1);
|
22366
|
-
let from = data[index], to = data[index + 1];
|
22434
|
+
let from = data[index], to = data[index + 1] || 0x10000;
|
22367
22435
|
if (next < from)
|
22368
22436
|
high = mid;
|
22369
22437
|
else if (next >= to)
|
@@ -23334,7 +23402,7 @@
|
|
23334
23402
|
spaceAfterDashes: false,
|
23335
23403
|
slashComments: false,
|
23336
23404
|
doubleQuotedStrings: false,
|
23337
|
-
|
23405
|
+
doubleDollarQuotedStrings: false,
|
23338
23406
|
unquotedBitLiterals: false,
|
23339
23407
|
treatBitsAsBytes: false,
|
23340
23408
|
charSetCasts: false,
|
@@ -23361,7 +23429,7 @@
|
|
23361
23429
|
input.advance();
|
23362
23430
|
input.acceptToken(whitespace);
|
23363
23431
|
}
|
23364
|
-
else if (next == 36 /* Ch.Dollar */ && input.next == 36 /* Ch.Dollar */ && d.
|
23432
|
+
else if (next == 36 /* Ch.Dollar */ && input.next == 36 /* Ch.Dollar */ && d.doubleDollarQuotedStrings) {
|
23365
23433
|
readDoubleDollarLiteral(input);
|
23366
23434
|
input.acceptToken(String$1);
|
23367
23435
|
}
|
@@ -23745,6 +23813,7 @@
|
|
23745
23813
|
static define(spec) {
|
23746
23814
|
let d = dialect(spec, spec.keywords, spec.types, spec.builtin);
|
23747
23815
|
let language = LRLanguage.define({
|
23816
|
+
name: "sql",
|
23748
23817
|
parser: parser.configure({
|
23749
23818
|
tokenizers: [{ from: tokens, to: tokensFor(d) }]
|
23750
23819
|
}),
|
@@ -23764,14 +23833,6 @@
|
|
23764
23833
|
return completeKeywords(dialect.dialect.words, upperCase);
|
23765
23834
|
}
|
23766
23835
|
/**
|
23767
|
-
FIXME remove on 1.0 @internal
|
23768
|
-
*/
|
23769
|
-
function keywordCompletion(dialect, upperCase = false) {
|
23770
|
-
return dialect.language.data.of({
|
23771
|
-
autocomplete: keywordCompletionSource(dialect, upperCase)
|
23772
|
-
});
|
23773
|
-
}
|
23774
|
-
/**
|
23775
23836
|
Returns a completion sources that provides schema-based completion
|
23776
23837
|
for the given configuration.
|
23777
23838
|
*/
|
@@ -23779,27 +23840,6 @@
|
|
23779
23840
|
return config.schema ? completeFromSchema(config.schema, config.tables, config.defaultTable, config.defaultSchema)
|
23780
23841
|
: () => null;
|
23781
23842
|
}
|
23782
|
-
/**
|
23783
|
-
FIXME remove on 1.0 @internal
|
23784
|
-
*/
|
23785
|
-
function schemaCompletion(config) {
|
23786
|
-
return config.schema ? (config.dialect || StandardSQL).language.data.of({
|
23787
|
-
autocomplete: schemaCompletionSource(config)
|
23788
|
-
}) : [];
|
23789
|
-
}
|
23790
|
-
/**
|
23791
|
-
SQL language support for the given SQL dialect, with keyword
|
23792
|
-
completion, and, if provided, schema-based completion as extra
|
23793
|
-
extensions.
|
23794
|
-
*/
|
23795
|
-
function sql(config = {}) {
|
23796
|
-
let lang = config.dialect || StandardSQL;
|
23797
|
-
return new LanguageSupport(lang.language, [schemaCompletion(config), keywordCompletion(lang, !!config.upperCaseKeywords)]);
|
23798
|
-
}
|
23799
|
-
/**
|
23800
|
-
The standard SQL dialect.
|
23801
|
-
*/
|
23802
|
-
const StandardSQL = /*@__PURE__*/SQLDialect.define({});
|
23803
23843
|
const MySQLKeywords = "accessible algorithm analyze asensitive authors auto_increment autocommit avg avg_row_length binlog btree cache catalog_name chain change changed checkpoint checksum class_origin client_statistics coalesce code collations columns comment committed completion concurrent consistent contains contributors convert database databases day_hour day_microsecond day_minute day_second delay_key_write delayed delimiter des_key_file dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile enable enclosed ends engine engines enum errors escaped even event events every explain extended fast field fields flush force found_rows fulltext grants handler hash high_priority hosts hour_microsecond hour_minute hour_second ignore ignore_server_ids import index index_statistics infile innodb insensitive insert_method install invoker iterate keys kill linear lines list load lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modify mutex mysql_errno no_write_to_binlog offline offset one online optimize optionally outfile pack_keys parser partition partitions password phase plugin plugins prev processlist profile profiles purge query quick range read_write rebuild recover regexp relaylog remove rename reorganize repair repeatable replace require resume rlike row_format rtree schedule schema_name schemas second_microsecond security sensitive separator serializable server share show slave slow snapshot soname spatial sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result ssl starting starts std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace terminated triggers truncate uncommitted uninstall unlock upgrade use use_frm user_resources user_statistics utc_date utc_time utc_timestamp variables views warnings xa xor year_month zerofill";
|
23804
23844
|
const MySQLTypes = SQLTypes + "bool blob long longblob longtext medium mediumblob mediumint mediumtext tinyblob tinyint tinytext text bigint int1 int2 int3 int4 int8 float4 float8 varbinary varcharacter precision datetime unsigned signed";
|
23805
23845
|
const MySQLBuiltin = "charset clear edit ego help nopager notee nowarning pager print prompt quit rehash source status system tee";
|
@@ -23869,7 +23909,6 @@
|
|
23869
23909
|
}
|
23870
23910
|
const type = 'text/plain';
|
23871
23911
|
let text = window.sqlFetch.result.columns.map((header) => {
|
23872
|
-
console.log(header.includes(delimiter));
|
23873
23912
|
if (typeof header === 'string' && (header.includes('"') || header.includes(delimiter))) {
|
23874
23913
|
return `"${header.replaceAll('"', '""')}"`
|
23875
23914
|
} else {
|
@@ -23912,10 +23951,35 @@
|
|
23912
23951
|
});
|
23913
23952
|
const schemas = Object.entries(window.metadata.schemas);
|
23914
23953
|
const editorSchema = {};
|
23954
|
+
const tables = [];
|
23955
|
+
const hasTableAliases = Object.keys(window.metadata.table_aliases).length > 0;
|
23915
23956
|
schemas.forEach(([schemaName, schema]) => {
|
23916
23957
|
Object.entries(schema.tables).forEach(([tableName, table]) => {
|
23917
23958
|
const qualifiedTableName = schemas.length === 1 ? tableName : `${schemaName}.${tableName}`;
|
23918
|
-
|
23959
|
+
const columns = Object.keys(table.columns);
|
23960
|
+
editorSchema[qualifiedTableName] = columns;
|
23961
|
+
const alias = window.metadata.table_aliases[tableName];
|
23962
|
+
if (alias) {
|
23963
|
+
editorSchema[alias] = columns;
|
23964
|
+
tables.push({
|
23965
|
+
label: qualifiedTableName,
|
23966
|
+
detail: alias,
|
23967
|
+
alias_type: 'with',
|
23968
|
+
quoted: '`' + qualifiedTableName + '` ' + alias,
|
23969
|
+
unquoted: `${qualifiedTableName} ${alias}`
|
23970
|
+
});
|
23971
|
+
tables.push({
|
23972
|
+
label: qualifiedTableName,
|
23973
|
+
detail: alias,
|
23974
|
+
alias_type: 'only',
|
23975
|
+
quoted: '`' + alias + '`',
|
23976
|
+
unquoted: alias
|
23977
|
+
});
|
23978
|
+
} else {
|
23979
|
+
tables.push({
|
23980
|
+
label: qualifiedTableName
|
23981
|
+
});
|
23982
|
+
}
|
23919
23983
|
});
|
23920
23984
|
});
|
23921
23985
|
// I prefer to use Cmd-Enter/Ctrl-Enter to submit the query. Here I am replacing the default mapping.
|
@@ -23948,6 +24012,80 @@
|
|
23948
24012
|
...completionKeymap,
|
23949
24013
|
...lintKeymap
|
23950
24014
|
]);
|
24015
|
+
const sqlConfig = {
|
24016
|
+
dialect: MySQL,
|
24017
|
+
upperCaseKeywords: true,
|
24018
|
+
schema: editorSchema,
|
24019
|
+
tables
|
24020
|
+
};
|
24021
|
+
const scs = schemaCompletionSource(sqlConfig);
|
24022
|
+
const sqlExtension = new LanguageSupport(
|
24023
|
+
MySQL.language,
|
24024
|
+
[
|
24025
|
+
MySQL.language.data.of({
|
24026
|
+
autocomplete: (context) => {
|
24027
|
+
const result = scs(context);
|
24028
|
+
if (!hasTableAliases || !result?.options) return result
|
24029
|
+
|
24030
|
+
const tree = syntaxTree(context.state);
|
24031
|
+
let node = tree.resolveInner(context.pos, -1);
|
24032
|
+
|
24033
|
+
// We are trying to identify the case where we are autocompleting a table name after "from" or "join"
|
24034
|
+
// TODO: we don't handle the case where a user typed "select table.foo from". In that case we probably
|
24035
|
+
// shouldn't autocomplete the alias. Though, if the user typed "select table.foo, t.bar", we won't know
|
24036
|
+
// what to do.
|
24037
|
+
// TODO: if table aliases aren't enabled, we don't need to override autocomplete.
|
24038
|
+
|
24039
|
+
if (node?.name === 'Statement') {
|
24040
|
+
// The node can be a Statement if the cursor is at the end of "from " and there is a complete
|
24041
|
+
// statement in the editor (semicolon present). In that case we want to find the node just before the
|
24042
|
+
// current position so that we can check whether it is "from" or "join".
|
24043
|
+
node = node.childBefore(context.pos);
|
24044
|
+
} else if (node?.name === 'Script') {
|
24045
|
+
// It seems the node can sometimes be a Script if the cursor is at the end of the last statement in the
|
24046
|
+
// editor and the statement doesn't end in a semicolon. In that case we can find the last statement in the
|
24047
|
+
// Script so that we can check whether it is "from" or "join".
|
24048
|
+
node = node.lastChild?.childBefore(context.pos);
|
24049
|
+
} else if (['Identifier', 'QuotedIdentifier', 'Keyword'].includes(node?.name)) {
|
24050
|
+
// If the node is an Identifier, we might be in the middle of typing the table name. If the node is a
|
24051
|
+
// Keyword but isn't "from" or "join", we might be in the middle of typing a table name that is similar
|
24052
|
+
// to a Keyword, for instance "orders" or "selections" or "fromages". In these cases, look for the previous
|
24053
|
+
// sibling so that we can check whether it is "from" or "join".
|
24054
|
+
node = node.prevSibling;
|
24055
|
+
}
|
24056
|
+
|
24057
|
+
const nodeText = node ? context.state.doc.sliceString(node.from, node.to).toLowerCase() : null;
|
24058
|
+
if (node?.name === 'Keyword' && ['from', 'join'].includes(nodeText)) {
|
24059
|
+
result.options = result.options.filter((option) => {
|
24060
|
+
return option.alias_type === undefined || option.alias_type === 'with'
|
24061
|
+
});
|
24062
|
+
} else {
|
24063
|
+
result.options = result.options.filter((option) => {
|
24064
|
+
return option.alias_type === undefined || option.alias_type === 'only'
|
24065
|
+
});
|
24066
|
+
}
|
24067
|
+
result.options = result.options.map((option) => {
|
24068
|
+
// Some shenanigans. If the default autocomplete function quoted the label, we want to ensure the quote
|
24069
|
+
// only applies to the table name and not the alias. You might think we could do this by overriding the
|
24070
|
+
// apply function but apply is set to null when quoting.
|
24071
|
+
// See https://github.com/codemirror/lang-sql/blob/ebf115fffdbe07f91465ccbd82868c587f8182bc/src/complete.ts#L90
|
24072
|
+
if (option.alias_type) {
|
24073
|
+
if (option.label.match(/^`.*`$/)) {
|
24074
|
+
option.apply = option.quoted;
|
24075
|
+
} else {
|
24076
|
+
option.apply = option.unquoted;
|
24077
|
+
}
|
24078
|
+
}
|
24079
|
+
return option
|
24080
|
+
});
|
24081
|
+
return result
|
24082
|
+
}
|
24083
|
+
}),
|
24084
|
+
MySQL.language.data.of({
|
24085
|
+
autocomplete: keywordCompletionSource(MySQL, true)
|
24086
|
+
})
|
24087
|
+
]
|
24088
|
+
);
|
23951
24089
|
window.editorView = new EditorView({
|
23952
24090
|
state: EditorState.create({
|
23953
24091
|
extensions: [
|
@@ -23969,11 +24107,7 @@
|
|
23969
24107
|
highlightActiveLine(),
|
23970
24108
|
highlightSelectionMatches(),
|
23971
24109
|
editorKeymap,
|
23972
|
-
|
23973
|
-
dialect: MySQL,
|
23974
|
-
upperCaseKeywords: true,
|
23975
|
-
schema: editorSchema
|
23976
|
-
}),
|
24110
|
+
sqlExtension,
|
23977
24111
|
fixedHeightEditor,
|
23978
24112
|
placeholder('Let\'s query!')
|
23979
24113
|
]
|
@@ -24161,6 +24295,12 @@
|
|
24161
24295
|
while (tablesElement.firstChild) {
|
24162
24296
|
tablesElement.removeChild(tablesElement.firstChild);
|
24163
24297
|
}
|
24298
|
+
while (columnsElement.firstChild) {
|
24299
|
+
columnsElement.removeChild(columnsElement.firstChild);
|
24300
|
+
}
|
24301
|
+
while (indexesElement.firstChild) {
|
24302
|
+
indexesElement.removeChild(indexesElement.firstChild);
|
24303
|
+
}
|
24164
24304
|
const schemaName = schemasElement.value;
|
24165
24305
|
const schema = window.metadata.schemas[schemaName];
|
24166
24306
|
const tableNames = Object.keys(schema.tables);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqlui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.39
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mysql2
|