Skip to content

Commit

Permalink
Refactored code mirror integration, better find and replace, add keyb…
Browse files Browse the repository at this point in the history
…oard shortcuts
  • Loading branch information
marshallino16 committed Apr 1, 2021
1 parent e5a3551 commit 91eb30d
Show file tree
Hide file tree
Showing 372 changed files with 42,239 additions and 1,940 deletions.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ CodeMirror-SwiftUI is a lightweight wrapper of CodeMirror for macOS and iOS pack
This package is a fork from [CodeMirror-Swift](https://github.com/ProxymanApp/CodeMirror-Swift)

## Features
- 🍭 Lightweight CodeMirror wrapper (build 5.57.0)
- 🍭 Lightweight CodeMirror wrapper (build 5.59.4)
- ✅ 100% Native Swift 5 and modern WKWebView
- 👑 Support iOS & macOS
- 🎧 Built-in addons
Expand Down Expand Up @@ -61,6 +61,15 @@ CodeView(theme: themes[selectedTheme],
## Details


#### Code Editor Features

- CodeMirror v5.59.4
- Better find and replace dialog integration
- Autocompletion hints (ctrl+space)
- Reindent (ctrl+i)
- Auto close tags and brackets


#### Parameters

CodeView has multiple params:
Expand Down Expand Up @@ -97,7 +106,7 @@ onContentChange { ... }

- [Snip](https://github.com/Pictarine/macos-snippets), a snippet manager for macOS is also using CodeMirror-SwiftUI

[![Screen Shot 2020-08-26 at 7 55 56 PM](https://user-images.githubusercontent.com/1506323/91417795-97808a00-e851-11ea-8100-c9d2b075b59d.png)](https://github.com/Pictarine/macos-snippets)
[![black_light_theme](https://user-images.githubusercontent.com/1506323/110323754-5560ae00-8015-11eb-98a3-7822772cbe00.png)](https://github.com/Pictarine/macos-snippets)


## License
Expand Down
25 changes: 21 additions & 4 deletions Sources/CodeMirror-SwiftUI/Model/Mode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import Foundation


public struct Mode: Equatable, Codable {

let name: String
let mimeType: String

}

public enum CodeMode: String {
Expand All @@ -33,6 +34,7 @@ public enum CodeMode: String {
case lisp
case css
case django
case dart
case dockerfile
case erlang
case fortran
Expand All @@ -49,6 +51,7 @@ public enum CodeMode: String {
case lua
case markdown
case maths
case ntriples
case pascal
case perl
case php
Expand All @@ -64,11 +67,13 @@ public enum CodeMode: String {
case shell
case sql
case sqllite
case sparql
case mysql
case latex
case swift
case text
case toml
case turtle
case vb
case vue
case xml
Expand All @@ -86,6 +91,7 @@ public enum CodeMode: String {
CodeMode.kotlin.mode(),
CodeMode.scala.mode(),
CodeMode.csharp.mode(),
CodeMode.dart.mode(),
CodeMode.java.mode(),
CodeMode.cobol.mode(),
CodeMode.coffeescript.mode(),
Expand All @@ -108,6 +114,7 @@ public enum CodeMode: String {
CodeMode.lua.mode(),
CodeMode.markdown.mode(),
CodeMode.maths.mode(),
CodeMode.ntriples.mode(),
CodeMode.pascal.mode(),
CodeMode.perl.mode(),
CodeMode.php.mode(),
Expand All @@ -123,11 +130,13 @@ public enum CodeMode: String {
CodeMode.shell.mode(),
CodeMode.sql.mode(),
CodeMode.sqllite.mode(),
CodeMode.sparql.mode(),
CodeMode.mysql.mode(),
CodeMode.latex.mode(),
CodeMode.swift.mode(),
CodeMode.text.mode(),
CodeMode.toml.mode(),
CodeMode.turtle.mode(),
CodeMode.vb.mode(),
CodeMode.vue.mode(),
CodeMode.xml.mode(),
Expand Down Expand Up @@ -175,6 +184,8 @@ public enum CodeMode: String {
return Mode(name: "css/scss", mimeType: "text/x-scss")
case .django:
return Mode(name: "django", mimeType: "text/x-django")
case .dart:
return Mode(name: "dart", mimeType: "application/dart")
case .dockerfile:
return Mode(name: "dockerfile", mimeType: "text/x-dockerfile")
case .erlang:
Expand Down Expand Up @@ -207,16 +218,18 @@ public enum CodeMode: String {
return Mode(name: "markdown", mimeType: "text/markdown")
case .maths:
return Mode(name: "maths", mimeType: "text/x-mathematica")
case .ntriples:
return Mode(name: "ntriples", mimeType: "application/n-triples")
case .pascal:
return Mode(name: "pascal", mimeType: "text/x-pascal")
case .perl:
return Mode(name: "perl", mimeType: "perl")
case .php:
return Mode(name: "php", mimeType: "application/x-httpd-php")
return Mode(name: "php", mimeType: "text/x-php")
case .powershell:
return Mode(name: "powershell", mimeType: "application/x-powershell")
case .properties:
return Mode(name: "properties", mimeType: "text/x-properties")
return Mode(name: "properties", mimeType: "text/x-properties")
case .protobuf:
return Mode(name: "protobuf", mimeType: "text/x-protobuf")
case .python:
Expand All @@ -237,6 +250,8 @@ public enum CodeMode: String {
return Mode(name: "sql", mimeType: "text/x-sql")
case .sqllite:
return Mode(name: "sqllite", mimeType: "text/x-sqlite")
case .sparql:
return Mode(name: "sparql", mimeType: "application/sparql-query")
case .mysql:
return Mode(name: "mysql", mimeType: "text/x-mysql")
case .latex:
Expand All @@ -246,7 +261,9 @@ public enum CodeMode: String {
case .text:
return Mode(name: "text", mimeType: "text/plain-text")
case .toml:
return Mode(name: "toml", mimeType: "text/x-toml")
return Mode(name: "toml", mimeType: "text/x-toml")
case .turtle:
return Mode(name: "turtle", mimeType: "text/turtle")
case .vb:
return Mode(name: "vb", mimeType: "text/x-vb")
case .vue:
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,8 +1,211 @@
'use strict';(function(h){"object"==typeof exports&&"object"==typeof module?h(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],h):h(CodeMirror)})(function(h){function v(b){b=b.search(q);return-1==b?0:b}function E(b,d,a){return/\bstring\b/.test(b.getTokenTypeAt(k(d.line,0)))&&!/^['"`]/.test(a)}function C(b,d){var a=b.getMode();return!1!==a.useInnerComments&&a.innerMode?b.getModeAt(d):a}var z={},q=/[^\s\u00a0]/,k=h.Pos;h.commands.toggleComment=function(b){b.toggleComment()};
h.defineExtension("toggleComment",function(b){b||(b=z);for(var d=Infinity,a=this.listSelections(),e=null,c=a.length-1;0<=c;c--){var f=a[c].from(),g=a[c].to();f.line>=d||(g.line>=d&&(g=k(d,0)),d=f.line,null==e?this.uncomment(f,g,b)?e="un":(this.lineComment(f,g,b),e="line"):"un"==e?this.uncomment(f,g,b):this.lineComment(f,g,b))}});h.defineExtension("lineComment",function(b,d,a){a||(a=z);var e=this,c=C(e,b),f=e.getLine(b.line);if(null!=f&&!E(e,b,f)){var g=a.lineComment||c.lineComment;if(g){var m=Math.min(0!=
d.ch||d.line==b.line?d.line+1:d.line,e.lastLine()+1),h=null==a.padding?" ":a.padding,l=a.commentBlankLines||b.line==d.line;e.operation(function(){if(a.indent){for(var d=null,c=b.line;c<m;++c){var f=e.getLine(c);f=f.slice(0,v(f));if(null==d||d.length>f.length)d=f}for(c=b.line;c<m;++c){f=e.getLine(c);var n=d.length;if(l||q.test(f))f.slice(0,n)!=d&&(n=v(f)),e.replaceRange(d+g+h,k(c,0),k(c,n))}}else for(c=b.line;c<m;++c)(l||q.test(e.getLine(c)))&&e.replaceRange(g+h,k(c,0))})}else if(a.blockCommentStart||
c.blockCommentStart)a.fullLines=!0,e.blockComment(b,d,a)}});h.defineExtension("blockComment",function(b,d,a){a||(a=z);var e=this,c=C(e,b),f=a.blockCommentStart||c.blockCommentStart,g=a.blockCommentEnd||c.blockCommentEnd;if(!f||!g)(a.lineComment||c.lineComment)&&0!=a.fullLines&&e.lineComment(b,d,a);else if(!/\bcomment\b/.test(e.getTokenTypeAt(k(b.line,0)))){var m=Math.min(d.line,e.lastLine());m!=b.line&&0==d.ch&&q.test(e.getLine(m))&&--m;var h=null==a.padding?" ":a.padding;b.line>m||e.operation(function(){if(0!=
a.fullLines){var l=q.test(e.getLine(m));e.replaceRange(h+g,k(m));e.replaceRange(f+h,k(b.line,0));var w=a.blockCommentLead||c.blockCommentLead;if(null!=w)for(var p=b.line+1;p<=m;++p)(p!=m||l)&&e.replaceRange(w+h,k(p,0))}else e.replaceRange(g,d),e.replaceRange(f,b)})}});h.defineExtension("uncomment",function(b,d,a){a||(a=z);var e=this,c=C(e,b),f=Math.min(0!=d.ch||d.line==b.line?d.line:d.line-1,e.lastLine()),g=Math.min(b.line,f),h=a.lineComment||c.lineComment,v=[],l=null==a.padding?" ":a.padding,w;a:if(h){for(var p=
g;p<=f;++p){var A=e.getLine(p),n=A.indexOf(h);-1<n&&!/comment/.test(e.getTokenTypeAt(k(p,n+1)))&&(n=-1);if(-1==n&&q.test(A))break a;if(-1<n&&q.test(A.slice(0,n)))break a;v.push(A)}e.operation(function(){for(var a=g;a<=f;++a){var b=v[a-g],c=b.indexOf(h),d=c+h.length;0>c||(b.slice(d,d+l.length)==l&&(d+=l.length),w=!0,e.replaceRange("",k(a,c),k(a,d)))}});if(w)return!0}var r=a.blockCommentStart||c.blockCommentStart,t=a.blockCommentEnd||c.blockCommentEnd;if(!r||!t)return!1;var D=a.blockCommentLead||c.blockCommentLead,
x=e.getLine(g),y=x.indexOf(r);if(-1==y)return!1;var B=f==g?x:e.getLine(f),u=B.indexOf(t,f==g?y+r.length:0);a=k(g,y+1);c=k(f,u+1);if(-1==u||!/comment/.test(e.getTokenTypeAt(a))||!/comment/.test(e.getTokenTypeAt(c))||-1<e.getRange(a,c,"\n").indexOf(t))return!1;c=x.lastIndexOf(r,b.ch);a=-1==c?-1:x.slice(0,b.ch).indexOf(t,c+r.length);if(-1!=c&&-1!=a&&a+t.length!=b.ch)return!1;a=B.indexOf(t,d.ch);b=B.slice(d.ch).lastIndexOf(r,a-d.ch);c=-1==a||-1==b?-1:d.ch+b;if(-1!=a&&-1!=c&&c!=d.ch)return!1;e.operation(function(){e.replaceRange("",
k(f,u-(l&&B.slice(u-l.length,u)==l?l.length:0)),k(f,u+t.length));var a=y+r.length;l&&x.slice(a,a+l.length)==l&&(a+=l.length);e.replaceRange("",k(g,y),k(g,a));if(D)for(a=g+1;a<=f;++a){var b=e.getLine(a),c=b.indexOf(D);if(-1!=c&&!q.test(b.slice(0,c))){var d=c+D.length;l&&b.slice(d,d+l.length)==l&&(d+=l.length);e.replaceRange("",k(a,c),k(a,d))}}});return!0})});
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(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";

var noOptions = {};
var nonWS = /[^\s\u00a0]/;
var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos;

function firstNonWS(str) {
var found = str.search(nonWS);
return found == -1 ? 0 : found;
}

CodeMirror.commands.toggleComment = function(cm) {
cm.toggleComment();
};

CodeMirror.defineExtension("toggleComment", function(options) {
if (!options) options = noOptions;
var cm = this;
var minLine = Infinity, ranges = this.listSelections(), mode = null;
for (var i = ranges.length - 1; i >= 0; i--) {
var from = ranges[i].from(), to = ranges[i].to();
if (from.line >= minLine) continue;
if (to.line >= minLine) to = Pos(minLine, 0);
minLine = from.line;
if (mode == null) {
if (cm.uncomment(from, to, options)) mode = "un";
else { cm.lineComment(from, to, options); mode = "line"; }
} else if (mode == "un") {
cm.uncomment(from, to, options);
} else {
cm.lineComment(from, to, options);
}
}
});

// Rough heuristic to try and detect lines that are part of multi-line string
function probablyInsideString(cm, pos, line) {
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
}

function getMode(cm, pos) {
var mode = cm.getMode()
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
}

CodeMirror.defineExtension("lineComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var firstLine = self.getLine(from.line);
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;

var commentString = options.lineComment || mode.lineComment;
if (!commentString) {
if (options.blockCommentStart || mode.blockCommentStart) {
options.fullLines = true;
self.blockComment(from, to, options);
}
return;
}

var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
var pad = options.padding == null ? " " : options.padding;
var blankLines = options.commentBlankLines || from.line == to.line;

self.operation(function() {
if (options.indent) {
var baseString = null;
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i);
var whitespace = line.slice(0, firstNonWS(line));
if (baseString == null || baseString.length > whitespace.length) {
baseString = whitespace;
}
}
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i), cut = baseString.length;
if (!blankLines && !nonWS.test(line)) continue;
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
}
} else {
for (var i = from.line; i < end; ++i) {
if (blankLines || nonWS.test(self.getLine(i)))
self.replaceRange(commentString + pad, Pos(i, 0));
}
}
});
});

CodeMirror.defineExtension("blockComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) {
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
self.lineComment(from, to, options);
return;
}
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return

var end = Math.min(to.line, self.lastLine());
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;

var pad = options.padding == null ? " " : options.padding;
if (from.line > end) return;

self.operation(function() {
if (options.fullLines != false) {
var lastLineHasText = nonWS.test(self.getLine(end));
self.replaceRange(pad + endString, Pos(end));
self.replaceRange(startString + pad, Pos(from.line, 0));
var lead = options.blockCommentLead || mode.blockCommentLead;
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
if (i != end || lastLineHasText)
self.replaceRange(lead + pad, Pos(i, 0));
} else {
var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected()
self.replaceRange(endString, to);
if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to)
self.replaceRange(startString, from);
}
});
});

CodeMirror.defineExtension("uncomment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);

// Try finding line comments
var lineString = options.lineComment || mode.lineComment, lines = [];
var pad = options.padding == null ? " " : options.padding, didSomething;
lineComment: {
if (!lineString) break lineComment;
for (var i = start; i <= end; ++i) {
var line = self.getLine(i);
var found = line.indexOf(lineString);
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
if (found == -1 && nonWS.test(line)) break lineComment;
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
lines.push(line);
}
self.operation(function() {
for (var i = start; i <= end; ++i) {
var line = lines[i - start];
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
if (pos < 0) continue;
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
didSomething = true;
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
}
});
if (didSomething) return true;
}

// Try block comments
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) return false;
var lead = options.blockCommentLead || mode.blockCommentLead;
var startLine = self.getLine(start), open = startLine.indexOf(startString)
if (open == -1) return false
var endLine = end == start ? startLine : self.getLine(end)
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
if (close == -1 ||
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
return false;

// Avoid killing block comments completely outside the selection.
// Positions of the last startString before the start of the selection, and the first endString after it.
var lastStart = startLine.lastIndexOf(startString, from.ch);
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
// Positions of the first endString after the end of the selection, and the last startString before it.
firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;

self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
Pos(end, close + endString.length));
var openEnd = open + startString.length;
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
if (lead) for (var i = start + 1; i <= end; ++i) {
var line = self.getLine(i), found = line.indexOf(lead);
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
var foundEnd = found + lead.length;
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
}
});
return true;
});
});
Loading

0 comments on commit 91eb30d

Please sign in to comment.