From e62b14c10a659c81ddf801d02e35f45d331f034a Mon Sep 17 00:00:00 2001 From: keforbes Date: Fri, 4 May 2018 15:34:50 -0600 Subject: [PATCH 001/102] add Change Operator and Visual Mode tutorials --- .../src/Services/Learning/Tutorial/Notes.tsx | 21 ++++ .../Tutorials/ChangeOperatorTutorial.tsx | 48 +++++++++ .../Tutorial/Tutorials/CopyPasteTutorial.tsx | 4 +- .../Tutorial/Tutorials/VisualModeTutorial.tsx | 102 ++++++++++++++++++ .../Learning/Tutorial/Tutorials/index.tsx | 4 + 5 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 browser/src/Services/Learning/Tutorial/Tutorials/ChangeOperatorTutorial.tsx create mode 100644 browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index 383a21d300..c69d625fb9 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -268,6 +268,27 @@ export const DeleteWordKey = (): JSX.Element => { ) } +export const ChangeOperatorKey = (): JSX.Element => { + return ( + + + motion: Change text specified by a `motion`. Examples: + + } + /> + ) +} +export const ChangeWordKey = (): JSX.Element => { + return ( + Delete to the end of the current word and enter Insert mode.} + /> + ) +} + export const HJKLKeys = (): JSX.Element => { return ( diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/ChangeOperatorTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/ChangeOperatorTutorial.tsx new file mode 100644 index 0000000000..1575502361 --- /dev/null +++ b/browser/src/Services/Learning/Tutorial/Tutorials/ChangeOperatorTutorial.tsx @@ -0,0 +1,48 @@ +/** + * ChangeOperatorTutorial.tsx + * + * Tutorial that exercises the change operator + */ + +import * as React from "react" + +import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" +import * as Notes from "./../Notes" +import * as Stages from "./../Stages" + +const Line1 = "The change operator can be used for quikcly fixing typos" +const Line1Marker = "The change operator can be used for ".length +const Line1Pending = "The change operator can be used for fixing typos" +const Line1Fixed = "The change operator can be used for quickly fixing typos" + +export class ChangeOperatorTutorial implements ITutorial { + private _stages: ITutorialStage[] + + constructor() { + this._stages = [ + new Stages.SetBufferStage([Line1]), + new Stages.MoveToGoalStage("Move to the goal marker", 0, Line1Marker), + new Stages.WaitForStateStage("Fix the typo by hitting 'cw'", [Line1Pending]), + new Stages.WaitForStateStage("Enter the word 'quickly'", [Line1Fixed]), + new Stages.WaitForModeStage("Exit Insert mode by hitting ", "normal"), + ] + } + + public get metadata(): ITutorialMetadata { + return { + id: "oni.tutorials.change_operator", + name: "Change Operator: c", + description: + "Now that you know about operators and motions pairing like a noun and a verb, we can start learning more operators. The `c` operator allows you to _change_ text. It deletes the selected text and immediately enters Insert mode so you can enter new text. The text to be changed is defined by any motion just like the delete operator. It might not seem very impressive right now but `c` will become more useful as you learn more motions.", + level: 210, + } + } + + public get stages(): ITutorialStage[] { + return this._stages + } + + public get notes(): JSX.Element[] { + return [, , ] + } +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx index 216ee39044..00419d11f6 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx @@ -76,8 +76,8 @@ export class CopyPasteTutorial implements ITutorial { id: "oni.tutorials.copy_paste", name: "Copy & Paste: y, p", description: - 'Now that you know about operators and motions pairing like a noun and a verb, we can start learning new operators. The `y` operator can be used to copy ("yank") text which can then be pasted with `p`. Using `p` pastes _after_ the cursor, and `P` pastes _before_ the cursor. The `y` operator behaves just like the `d` operator and can be paired with any motion.', - level: 210, + "Now that you know the delete and change operators, let's learn vim's final operator: `y`. The `y` operator can be used to copy (\"yank\") text which can then be pasted with `p`. Using `p` pastes _after_ the cursor, and `P` pastes _before_ the cursor. The `y` operator behaves just like the `d` and `c` operators and can be paired with any motion.", + level: 220, } } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx new file mode 100644 index 0000000000..ed2b7b02e0 --- /dev/null +++ b/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx @@ -0,0 +1,102 @@ +/** + * VisualModeTutorial.tsx + * + * Tutorial for learning how to select text in visual mode. + */ + +import * as React from "react" + +import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" +import * as Notes from "./../Notes" +import * as Stages from "./../Stages" + +const Line1 = "Text can be selectedselected with 'v'." +const Line2 = "Selected text can then be (y)anked, (c)hanged, or (d)eleted with a single keypress" +const Line3 = "To select entire lines, use 'V' to include line-ending characters." +const Line4 = "Selected lines can also be with a single keypress" +const Line1Marker = "Text can be ".length +const Line1Marker2 = "Text can be selecte".length +const Line1Change = "Text can be selected with 'v'." +const Line2Marker = "Selected text can then be ".length +const Line2Marker2 = "Selected text can then be (y)anked, (c)hanged, or (d)elete".length +const Line4Marker = "Selected lines can also be".length +const Line4PostPaste = + "Selected lines can also be (y)anked, (c)hanged, or (d)eleted with a single keypress" +const Line3Marker = "To select entire lines, use 'V' to include ".length +const Line3Marker2 = "To select entire lines, use 'V' to include line-endin".length +const Line3Pending = "To select entire lines, use 'V' to include characters." +const Line3Change = "To select entire lines, use 'V' to include newline characters." +const Line3Marker3 = "To select entire lines, use 'V' to include newlin".length + +export class VisualModeTutorial implements ITutorial { + private _stages: ITutorialStage[] + + constructor() { + this._stages = [ + new Stages.SetBufferStage([Line1, Line2, Line3, Line4]), + new Stages.MoveToGoalStage("Move to the goal marker", 0, Line1Marker), + new Stages.WaitForModeStage("Change to Visual mode with 'v'", "visual"), + new Stages.MoveToGoalStage("Move to the goal marker", 0, Line1Marker2), + new Stages.WaitForStateStage("Hit 'd' to delete the selected text", [ + Line1Change, + Line2, + Line3, + Line4, + ]), + new Stages.MoveToGoalStage("Move to the goal marker", 1, Line2Marker), + new Stages.WaitForModeStage("Change to Visual mode with 'v'", "visual"), + new Stages.MoveToGoalStage("Move to the goal marker", 1, Line2Marker2), + new Stages.WaitForRegisterStage( + "Yank the selection with 'y'", + "(y)anked, (c)hanged, or (d)eleted", + ), + new Stages.MoveToGoalStage("Move to the goal marker", 3, Line4Marker), + new Stages.WaitForStateStage("Paste the yanked text with 'p'", [ + Line1Change, + Line2, + Line3, + Line4PostPaste, + ]), + new Stages.MoveToGoalStage("Move to the goal marker", 2, Line3Marker), + new Stages.WaitForModeStage("Change to Visual mode with 'v'", "visual"), + new Stages.MoveToGoalStage("Move to the goal marker", 2, Line3Marker2), + new Stages.WaitForStateStage("Change the selected text with 'c'", [ + Line1Change, + Line2, + Line3Pending, + Line4PostPaste, + ]), + new Stages.WaitForStateStage("Enter the word 'newline'", [ + Line1Change, + Line2, + Line3Change, + Line4PostPaste, + ]), + new Stages.WaitForModeStage("Exit Insert mode by hitting ", "normal"), + new Stages.WaitForModeStage("Move into Visual Line mode with 'V'", "visual"), + new Stages.MoveToGoalStage("Move to the next line", 3, Line3Marker3), + new Stages.WaitForStateStage("Delete the selected lines with 'd'", [ + Line1Change, + Line2, + ]), + ] + } + + public get metadata(): ITutorialMetadata { + return { + id: "oni.tutorials.visual_mode", + name: "Visual Select: v, V", + description: + "Sometimes the text you want to modify isn't on a simple word boundary. We often need to change, yank, or delete any arbitrary text. Rather than performing a cursor movement and hoping you affected the correct characters, you can visually select text. Using 'v' will select characters as you move, and 'V' will select lines", + level: 230, + } + } + + public get stages(): ITutorialStage[] { + return this._stages + } + + public get notes(): JSX.Element[] { + return [, , , ] + } +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx index 1f679b8785..44136f7da1 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx @@ -6,6 +6,7 @@ import { ITutorial } from "./../ITutorial" import { BasicMovementTutorial } from "./BasicMovementTutorial" import { BeginningsAndEndingsTutorial } from "./BeginningsAndEndingsTutorial" +import { ChangeOperatorTutorial } from "./ChangeOperatorTutorial" import { CopyPasteTutorial } from "./CopyPasteTutorial" import { DeleteCharacterTutorial } from "./DeleteCharacterTutorial" import { DeleteOperatorTutorial } from "./DeleteOperatorTutorial" @@ -13,6 +14,7 @@ import { MoveAndInsertTutorial } from "./MoveAndInsertTutorial" import { SearchInBufferTutorial } from "./SearchInBufferTutorial" import { SwitchModeTutorial } from "./SwitchModeTutorial" import { VerticalMovementTutorial } from "./VerticalMovementTutorial" +import { VisualModeTutorial } from "./VisualModeTutorial" import { WordMotionTutorial } from "./WordMotionTutorial" export * from "./DeleteCharacterTutorial" @@ -29,4 +31,6 @@ export const AllTutorials: ITutorial[] = [ new WordMotionTutorial(), new SearchInBufferTutorial(), new CopyPasteTutorial(), + new ChangeOperatorTutorial(), + new VisualModeTutorial(), ] From df423f797ea45425f44ab2270008ee6406fee2c2 Mon Sep 17 00:00:00 2001 From: keforbes Date: Fri, 4 May 2018 15:40:51 -0600 Subject: [PATCH 002/102] add some Notes for visual mode --- .../src/Services/Learning/Tutorial/Notes.tsx | 17 +++++++++++++++++ .../Tutorial/Tutorials/VisualModeTutorial.tsx | 9 ++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index c69d625fb9..7f447efda6 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -345,3 +345,20 @@ export const PasteKey = (): JSX.Element => { Paste BEFORE the cursor} /> ) } + +export const VisualModeKey = (): JSX.Element => { + return ( + Move into Visual mode for selecting text} + /> + ) +} +export const VisualLineModeKey = (): JSX.Element => { + return ( + Move into line-wise Visual mode for selecting lines} + /> + ) +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx index ed2b7b02e0..f7111346f4 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/VisualModeTutorial.tsx @@ -97,6 +97,13 @@ export class VisualModeTutorial implements ITutorial { } public get notes(): JSX.Element[] { - return [, , , ] + return [ + , + , + , + , + , + , + ] } } From b2cfda87d909d73e909bdae50cff4d206eb08301 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 4 May 2018 16:10:39 -0700 Subject: [PATCH 003/102] Add Kim Fiedler as a backer - thank you! :) --- .github/config.yml | 1 + BACKERS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/config.yml b/.github/config.yml index 289126c333..37331175b7 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -30,3 +30,4 @@ backers: - 1718128 - 2042893 - 14060883 +- 244396 diff --git a/BACKERS.md b/BACKERS.md index 0e56c65d35..8ef8fe5190 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -116,6 +116,7 @@ Thanks you to all our backers for making Oni possible! * Patrick Massot * Jerome Pellois * Wesley Moore +* Kim Fiedler From af90c2644c1cfd4e9a97d5ef57eee1f3f4cbdff3 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 4 May 2018 17:00:35 -0700 Subject: [PATCH 004/102] Add Tom Boland as a backer - thank you! :) --- .github/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/config.yml b/.github/config.yml index 37331175b7..af80e47128 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -31,3 +31,5 @@ backers: - 2042893 - 14060883 - 244396 +- 8832878 + From 597509c4c3c0256ac24308294b4a47566a23d672 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 4 May 2018 18:15:27 -0700 Subject: [PATCH 005/102] Bump version to 0.3.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b8802ad25..3f84533d55 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "author": "", "email": "bryphe@outlook.com", "homepage": "https://www.onivim.io", - "version": "0.3.4", + "version": "0.3.5", "description": "Code editor with a modern twist on modal editing - powered by neovim.", "keywords": ["vim", "neovim", "text", "editor", "ide", "vim"], "main": "./lib/main/src/main.js", From 19507c478b58cff8390b8191ea74741803523501 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 4 May 2018 18:15:49 -0700 Subject: [PATCH 006/102] Upgrade electron to 1.8.6 (#2160) --- package.json | 378 ++++++++++++++++++++++++++++++++++++--------------- yarn.lock | 6 +- 2 files changed, 274 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 3f84533d55..45f29c8973 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,14 @@ "homepage": "https://www.onivim.io", "version": "0.3.5", "description": "Code editor with a modern twist on modal editing - powered by neovim.", - "keywords": ["vim", "neovim", "text", "editor", "ide", "vim"], + "keywords": [ + "vim", + "neovim", + "text", + "editor", + "ide", + "vim" + ], "main": "./lib/main/src/main.js", "bin": { "oni": "./cli/oni", @@ -44,23 +51,39 @@ "mac": { "artifactName": "${productName}-${version}-osx.${ext}", "category": "public.app-category.developer-tools", - "target": ["dmg"], - "files": ["bin/osx/**/*"] + "target": [ + "dmg" + ], + "files": [ + "bin/osx/**/*" + ] }, "linux": { "artifactName": "${productName}-${version}-${arch}-linux.${ext}", "maintainer": "bryphe@outlook.com", - "target": ["tar.gz", "deb", "rpm"] + "target": [ + "tar.gz", + "deb", + "rpm" + ] }, "win": { - "target": ["zip", "dir"], - "files": ["bin/x86/**/*"] + "target": [ + "zip", + "dir" + ], + "files": [ + "bin/x86/**/*" + ] }, "fileAssociations": [ { "name": "ADA source", "role": "Editor", - "ext": ["adb", "ads"] + "ext": [ + "adb", + "ads" + ] }, { "name": "Compiled AppleScript", @@ -80,12 +103,20 @@ { "name": "ASP document", "role": "Editor", - "ext": ["asp", "asa"] + "ext": [ + "asp", + "asa" + ] }, { "name": "ASP.NET document", "role": "Editor", - "ext": ["aspx", "ascx", "asmx", "ashx"] + "ext": [ + "aspx", + "ascx", + "asmx", + "ashx" + ] }, { "name": "BibTeX bibliography", @@ -100,7 +131,13 @@ { "name": "C++ source", "role": "Editor", - "ext": ["cc", "cp", "cpp", "cxx", "c++"] + "ext": [ + "cc", + "cp", + "cpp", + "cxx", + "c++" + ] }, { "name": "C# source", @@ -125,7 +162,10 @@ { "name": "Clojure source", "role": "Editor", - "ext": ["clj", "cljs"] + "ext": [ + "clj", + "cljs" + ] }, { "name": "Comma separated values", @@ -140,12 +180,20 @@ { "name": "CGI script", "role": "Editor", - "ext": ["cgi", "fcgi"] + "ext": [ + "cgi", + "fcgi" + ] }, { "name": "Configuration file", "role": "Editor", - "ext": ["cfg", "conf", "config", "htaccess"] + "ext": [ + "cfg", + "conf", + "config", + "htaccess" + ] }, { "name": "Cascading style sheet", @@ -170,7 +218,10 @@ { "name": "Erlang source", "role": "Editor", - "ext": ["erl", "hrl"] + "ext": [ + "erl", + "hrl" + ] }, { "name": "F-Script source", @@ -180,17 +231,32 @@ { "name": "Fortran source", "role": "Editor", - "ext": ["f", "for", "fpp", "f77", "f90", "f95"] + "ext": [ + "f", + "for", + "fpp", + "f77", + "f90", + "f95" + ] }, { "name": "Header", "role": "Editor", - "ext": ["h", "pch"] + "ext": [ + "h", + "pch" + ] }, { "name": "C++ header", "role": "Editor", - "ext": ["hh", "hpp", "hxx", "h++"] + "ext": [ + "hh", + "hpp", + "hxx", + "h++" + ] }, { "name": "Go source", @@ -200,17 +266,28 @@ { "name": "GTD document", "role": "Editor", - "ext": ["gtd", "gtdlog"] + "ext": [ + "gtd", + "gtdlog" + ] }, { "name": "Haskell source", "role": "Editor", - "ext": ["hs", "lhs"] + "ext": [ + "hs", + "lhs" + ] }, { "name": "HTML document", "role": "Editor", - "ext": ["htm", "html", "phtml", "shtml"] + "ext": [ + "htm", + "html", + "phtml", + "shtml" + ] }, { "name": "Include file", @@ -250,7 +327,10 @@ { "name": "JavaScript source", "role": "Editor", - "ext": ["js", "htc"] + "ext": [ + "js", + "htc" + ] }, { "name": "Java Server Page", @@ -275,7 +355,14 @@ { "name": "Lisp source", "role": "Editor", - "ext": ["lisp", "cl", "l", "lsp", "mud", "el"] + "ext": [ + "lisp", + "cl", + "l", + "lsp", + "mud", + "el" + ] }, { "name": "Log file", @@ -295,7 +382,12 @@ { "name": "Markdown document", "role": "Editor", - "ext": ["markdown", "mdown", "markdn", "md"] + "ext": [ + "markdown", + "mdown", + "markdn", + "md" + ] }, { "name": "Makefile source", @@ -305,17 +397,29 @@ { "name": "Mediawiki document", "role": "Editor", - "ext": ["wiki", "wikipedia", "mediawiki"] + "ext": [ + "wiki", + "wikipedia", + "mediawiki" + ] }, { "name": "MIPS assembler source", "role": "Editor", - "ext": ["s", "mips", "spim", "asm"] + "ext": [ + "s", + "mips", + "spim", + "asm" + ] }, { "name": "Modula-3 source", "role": "Editor", - "ext": ["m3", "cm3"] + "ext": [ + "m3", + "cm3" + ] }, { "name": "MoinMoin document", @@ -335,17 +439,28 @@ { "name": "OCaml source", "role": "Editor", - "ext": ["ml", "mli", "mll", "mly"] + "ext": [ + "ml", + "mli", + "mll", + "mly" + ] }, { "name": "Mustache document", "role": "Editor", - "ext": ["mustache", "hbs"] + "ext": [ + "mustache", + "hbs" + ] }, { "name": "Pascal source", "role": "Editor", - "ext": ["pas", "p"] + "ext": [ + "pas", + "p" + ] }, { "name": "Patch file", @@ -355,7 +470,11 @@ { "name": "Perl source", "role": "Editor", - "ext": ["pl", "pod", "perl"] + "ext": [ + "pl", + "pod", + "perl" + ] }, { "name": "Perl module", @@ -365,47 +484,80 @@ { "name": "PHP source", "role": "Editor", - "ext": ["php", "php3", "php4", "php5"] + "ext": [ + "php", + "php3", + "php4", + "php5" + ] }, { "name": "PostScript source", "role": "Editor", - "ext": ["ps", "eps"] + "ext": [ + "ps", + "eps" + ] }, { "name": "Property list", "role": "Editor", - "ext": ["dict", "plist", "scriptSuite", "scriptTerminology"] + "ext": [ + "dict", + "plist", + "scriptSuite", + "scriptTerminology" + ] }, { "name": "Python source", "role": "Editor", - "ext": ["py", "rpy", "cpy", "python"] + "ext": [ + "py", + "rpy", + "cpy", + "python" + ] }, { "name": "R source", "role": "Editor", - "ext": ["r", "s"] + "ext": [ + "r", + "s" + ] }, { "name": "Ragel source", "role": "Editor", - "ext": ["rl", "ragel"] + "ext": [ + "rl", + "ragel" + ] }, { "name": "Remind document", "role": "Editor", - "ext": ["rem", "remind"] + "ext": [ + "rem", + "remind" + ] }, { "name": "reStructuredText document", "role": "Editor", - "ext": ["rst", "rest"] + "ext": [ + "rst", + "rest" + ] }, { "name": "HTML with embedded Ruby", "role": "Editor", - "ext": ["rhtml", "erb"] + "ext": [ + "rhtml", + "erb" + ] }, { "name": "SQL with embedded Ruby", @@ -415,17 +567,28 @@ { "name": "Ruby source", "role": "Editor", - "ext": ["rb", "rbx", "rjs", "rxml"] + "ext": [ + "rb", + "rbx", + "rjs", + "rxml" + ] }, { "name": "Sass source", "role": "Editor", - "ext": ["sass", "scss"] + "ext": [ + "sass", + "scss" + ] }, { "name": "Scheme source", "role": "Editor", - "ext": ["scm", "sch"] + "ext": [ + "scm", + "sch" + ] }, { "name": "Setext document", @@ -473,7 +636,10 @@ { "name": "SWIG source", "role": "Editor", - "ext": ["i", "swg"] + "ext": [ + "i", + "swg" + ] }, { "name": "Tcl source", @@ -483,12 +649,20 @@ { "name": "TeX document", "role": "Editor", - "ext": ["tex", "sty", "cls"] + "ext": [ + "tex", + "sty", + "cls" + ] }, { "name": "Plain text document", "role": "Editor", - "ext": ["text", "txt", "utf8"] + "ext": [ + "text", + "txt", + "utf8" + ] }, { "name": "Textile document", @@ -508,17 +682,32 @@ { "name": "XML document", "role": "Editor", - "ext": ["xml", "xsd", "xib", "rss", "tld", "pt", "cpt", "dtml"] + "ext": [ + "xml", + "xsd", + "xib", + "rss", + "tld", + "pt", + "cpt", + "dtml" + ] }, { "name": "XSL stylesheet", "role": "Editor", - "ext": ["xsl", "xslt"] + "ext": [ + "xsl", + "xslt" + ] }, { "name": "Electronic business card", "role": "Editor", - "ext": ["vcf", "vcard"] + "ext": [ + "vcf", + "vcard" + ] }, { "name": "Visual Basic source", @@ -528,7 +717,10 @@ { "name": "YAML document", "role": "Editor", - "ext": ["yaml", "yml"] + "ext": [ + "yaml", + "yml" + ] }, { "name": "Text document", @@ -590,95 +782,67 @@ "scripts": { "precommit": "pretty-quick --staged", "prepush": "npm run build && npm run lint", - "build": - "npm run build:browser && npm run build:webview_preload && npm run build:main && npm run build:plugins", + "build": "npm run build:browser && npm run build:webview_preload && npm run build:main && npm run build:plugins", "build-debug": "npm run build:browser-debug && npm run build:main && npm run build:plugins", "build:browser": "webpack --config browser/webpack.production.config.js", "build:browser-debug": "webpack --config browser/webpack.debug.config.js", "build:main": "cd main && tsc -p tsconfig.json", - "build:plugins": - "npm run build:plugin:oni-plugin-typescript && npm run build:plugin:oni-plugin-markdown-preview", + "build:plugins": "npm run build:plugin:oni-plugin-typescript && npm run build:plugin:oni-plugin-markdown-preview", "build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && npm run build", - "build:plugin:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && npm run build", + "build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && npm run build", "build:test": "npm run build:test:unit && npm run build:test:integration", "build:test:integration": "cd test && tsc -p tsconfig.json", "build:test:unit": "npm run build:test:unit:browser && npm run build:test:unit:main", - "build:test:unit:browser": - "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", + "build:test:unit:browser": "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", "build:test:unit:main": "rimraf lib_test/main && cd main && tsc -p tsconfig.test.json", "build:webview_preload": "cd webview_preload && tsc -p tsconfig.json", "check-cached-binaries": "node build/script/CheckBinariesForBuild.js", "copy-icons": "node build/CopyIcons.js", - "debug:test:unit:browser": - "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", - "demo:screenshot": - "npm run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", - "demo:video": - "npm run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", - "dist:win:x86": - "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", - "dist:win:x64": - "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", - "pack:win": - "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", + "debug:test:unit:browser": "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "demo:screenshot": "npm run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", + "demo:video": "npm run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", + "dist:win:x86": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", + "dist:win:x64": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", + "pack:win": "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", "test": "npm run test:unit && npm run test:integration", "test:integration": "npm run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", "test:react": "jest --config ./jest.config.js ./ui-tests", "test:react:watch": "jest --config ./jest.config.js ./ui-tests --watch", "test:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage", - "test:unit:browser": - "npm run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "test:unit:browser": "npm run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", "test:unit": "npm run test:unit:browser && npm run test:unit:main && npm run test:react", - "test:unit:main": - "npm run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", + "test:unit:main": "npm run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", "upload:dist": "node build/script/UploadDistributionBuildsToAzure", "fix-lint": "npm run fix-lint:browser && npm run fix-lint:main && npm run fix-lint:test", - "fix-lint:browser": - "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "fix-lint:browser": "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "fix-lint:main": "tslint --fix --project main/tsconfig.json --config tslint.json", "fix-lint:test": "tslint --fix --project test/tsconfig.json --config tslint.json", "lint": "npm run lint:browser && npm run lint:main && npm run lint:test", - "lint:browser": - "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "lint:browser": "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "lint:main": "tslint --project main/tsconfig.json --config tslint.json", - "lint:test": - "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", + "lint:test": "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", "pack": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --publish never", - "ccov:instrument": - "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", - "ccov:test:browser": - "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", - "ccov:remap:browser:html": - "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", - "ccov:remap:browser:lcov": - "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", + "ccov:instrument": "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", + "ccov:test:browser": "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", + "ccov:remap:browser:html": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", + "ccov:remap:browser:lcov": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", "ccov:clean": "rimraf coverage", "ccov:upload": "codecov", "launch": "electron lib/main/src/main.js", - "start": - "concurrently --kill-others \"npm run start-hot\" \"npm run watch:browser\" \"npm run watch:plugins\"", - "start-hot": - "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", + "start": "concurrently --kill-others \"npm run start-hot\" \"npm run watch:browser\" \"npm run watch:plugins\"", + "start-hot": "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", "start-not-dev": "cross-env electron main.js", - "watch:browser": - "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", - "watch:plugins": - "npm run watch:plugins:oni-plugin-typescript && npm run watch:plugins:oni-plugin-markdown-preview", + "watch:browser": "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", + "watch:plugins": "npm run watch:plugins:oni-plugin-typescript && npm run watch:plugins:oni-plugin-markdown-preview", "watch:plugins:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && tsc --watch", - "watch:plugins:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && tsc --watch", + "watch:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && tsc --watch", "uninstall-global": "npm rm -g oni-vim", "install-global": "npm install -g oni-vim", - "install:plugins": - "npm run install:plugins:oni-plugin-markdown-preview && npm run install:plugins:oni-plugin-prettier", - "install:plugins:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && npm install --prod", - "install:plugins:oni-plugin-prettier": - "cd extensions/oni-plugin-prettier && npm install --prod", + "install:plugins": "npm run install:plugins:oni-plugin-markdown-preview && npm run install:plugins:oni-plugin-prettier", + "install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && npm install --prod", + "install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && npm install --prod", "postinstall": "npm run install:plugins && electron-rebuild && opencollective postinstall", - "profile:webpack": - "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" + "profile:webpack": "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" }, "repository": { "type": "git", @@ -719,13 +883,13 @@ }, "devDependencies": { "@types/chokidar": "^1.7.5", - "@types/fs-extra": "^5.0.2", "@types/classnames": "0.0.32", "@types/color": "2.0.0", "@types/detect-indent": "^5.0.0", "@types/dompurify": "^0.0.31", "@types/electron-settings": "^3.1.1", "@types/enzyme": "^3.1.8", + "@types/fs-extra": "^5.0.2", "@types/jest": "^22.1.3", "@types/jsdom": "11.0.0", "@types/lodash": "4.14.38", @@ -764,7 +928,7 @@ "cross-env": "3.1.3", "css-loader": "0.28.4", "detect-indent": "^5.0.0", - "electron": "^1.8.4", + "electron": "1.8.6", "electron-builder": "^20.5.1", "electron-devtools-installer": "^2.2.3", "electron-mocha": "5.0.0", diff --git a/yarn.lock b/yarn.lock index 99590d45cc..c73f7fb56a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3503,9 +3503,9 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@^1.8.4: - version "1.8.4" - resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.4.tgz#cca8d0e6889f238f55b414ad224f03e03b226a38" +electron@1.8.6: + version "1.8.6" + resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.6.tgz#6d45e377b321a35b78a794b64e40b2cf8face6ae" dependencies: "@types/node" "^8.0.24" electron-download "^3.0.1" From 4cd597b5fa2142ef8b1577b3b6c68c7454bab366 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 4 May 2018 18:37:51 -0700 Subject: [PATCH 007/102] Fix #2147 - Closed splits eating window space when 'editor.split.mode' is 'oni' (#2159) * Add test case that exercises the bug * Fix #2147 - remove empty split containers from the window manager --- .../WindowManager/LinearSplitProvider.ts | 6 ++- .../WindowManager/WindowManagerTests.ts | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/WindowManager/LinearSplitProvider.ts b/browser/src/Services/WindowManager/LinearSplitProvider.ts index ff976dd738..012b76d2b0 100644 --- a/browser/src/Services/WindowManager/LinearSplitProvider.ts +++ b/browser/src/Services/WindowManager/LinearSplitProvider.ts @@ -160,10 +160,14 @@ export class LinearSplitProvider implements IWindowSplitProvider { } public getState(): ISplitInfo { + if (this._splitProviders.length === 0) { + return null + } + return { type: "Split", direction: this._direction, - splits: this._splitProviders.map(sp => sp.getState()), + splits: this._splitProviders.map(sp => sp.getState()).filter(s => s !== null), } } diff --git a/browser/test/Services/WindowManager/WindowManagerTests.ts b/browser/test/Services/WindowManager/WindowManagerTests.ts index 1055f0f4b9..8e836d6c90 100644 --- a/browser/test/Services/WindowManager/WindowManagerTests.ts +++ b/browser/test/Services/WindowManager/WindowManagerTests.ts @@ -74,4 +74,42 @@ describe("WindowManagerTests", () => { ) assert.strictEqual(firstChild.splits.length, 2, "Validate both windows are in this split") }) + + it("#2147 - doesn't leave a split container after closing", async () => { + const split1 = new MockWindowSplit("window1") + const split2 = new MockWindowSplit("window2") + const split3 = new MockWindowSplit("window3") + + windowManager.createSplit("horizontal", split1) + + const split2Handle = windowManager.createSplit("vertical", split2, split1) + const split3Handle = windowManager.createSplit("horizontal", split3, split2) + + split2Handle.close() + split3Handle.close() + + const splitRoot = windowManager.splitRoot + const firstChild = splitRoot.splits[0] as ISplitInfo + + assert.strictEqual(splitRoot.type, "Split") + assert.strictEqual(splitRoot.direction, "vertical") + assert.strictEqual(splitRoot.splits.length, 1) + + assert.strictEqual(firstChild.type, "Split") + assert.strictEqual( + firstChild.direction, + "horizontal", + "Validate the splits are arranged horizontally (it's confusing... but this means they are vertical splits)", + ) + assert.strictEqual( + firstChild.splits.length, + 1, + "Validate there is only a single child in the 'horizontal' split.", + ) + assert.strictEqual( + firstChild.splits[0].type, + "Leaf", + "Validate the child of the 'horizontal' split is a leaf node.", + ) + }) }) From 6728b44738cd604b6f64df1675fcc94929e1cf08 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Sat, 5 May 2018 15:23:56 -0700 Subject: [PATCH 008/102] Fix up 'scrollIntoView' logic for tab (#2171) --- browser/src/UI/components/Tabs.tsx | 61 ++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/browser/src/UI/components/Tabs.tsx b/browser/src/UI/components/Tabs.tsx index d804ee78d2..ce2d7400f8 100644 --- a/browser/src/UI/components/Tabs.tsx +++ b/browser/src/UI/components/Tabs.tsx @@ -65,6 +65,16 @@ const InnerName = styled.span` ` export class Tabs extends React.PureComponent { + private _boundOnSelect: (tabId: number) => void + private _boundOnClose: (tabId: number) => void + + constructor(props: ITabsProps) { + super(props) + + this._boundOnSelect = (id: number) => this._onSelect(id) + this._boundOnClose = (id: number) => this._onClickClose(id) + } + public render(): JSX.Element { if (!this.props.visible) { return null @@ -88,8 +98,8 @@ export class Tabs extends React.PureComponent { this._onSelect(t.id)} - onClickClose={() => this._onClickClose(t.id)} + onClickName={this._boundOnSelect} + onClickClose={this._boundOnClose} backgroundColor={this.props.backgroundColor} foregroundColor={this.props.foregroundColor} height={this.props.height} @@ -115,8 +125,8 @@ export class Tabs extends React.PureComponent { } export interface ITabPropsWithClick extends ITabProps { - onClickName: () => void - onClickClose: () => void + onClickName: (id: number) => void + onClickClose: (id: number) => void backgroundColor: string foregroundColor: string @@ -138,19 +148,17 @@ interface IChromeDivElement extends HTMLDivElement { scrollIntoViewIfNeeded: (args: { behavior: string; block: string; inline: string }) => void } -export class Tab extends React.Component { +export class Tab extends React.PureComponent { private _tab: IChromeDivElement - public componentWillReceiveProps(next: ITabPropsWithClick) { - if (next.isSelected && this._tab) { - if (this._tab.scrollIntoViewIfNeeded) { - this._tab.scrollIntoViewIfNeeded({ - behavior: "smooth", - block: "center", - inline: "center", - }) - } - } + + public componentDidUpdate() { + this._checkIfShouldScroll() } + + public componentDidMount(): void { + this._checkIfShouldScroll() + } + public render() { const cssClasses = classNames("tab", { selected: this.props.isSelected, @@ -168,24 +176,27 @@ export class Tab extends React.Component { } return ( - this.props.onClickName()}> + (this._tab = e)} className={cssClasses} title={this.props.description} style={style} > -
+
this.props.onClickName(this.props.id)}>
-
+
this.props.onClickName(this.props.id)}> {this.props.name}
-
+
this.props.onClickClose(this.props.id)} + >
@@ -197,6 +208,18 @@ export class Tab extends React.Component { ) } + + private _checkIfShouldScroll(): void { + if (this.props.isSelected && this._tab) { + if (this._tab.scrollIntoViewIfNeeded) { + this._tab.scrollIntoViewIfNeeded({ + behavior: "smooth", + block: "center", + inline: "center", + }) + } + } + } } export const getTabName = (name: string, isDuplicate?: boolean): string => { From 54e2959155573043f50081b47534df937a45d01d Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Sun, 6 May 2018 13:02:09 -0700 Subject: [PATCH 009/102] Sidebar - Plugins: First round of improvements for plugins pane (#2172) * Make expand / collapse of plugins work * Start adding a component for plugin items --- browser/src/Plugins/PluginSidebarPane.tsx | 98 +++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/browser/src/Plugins/PluginSidebarPane.tsx b/browser/src/Plugins/PluginSidebarPane.tsx index fb828d68b9..34f27adac7 100644 --- a/browser/src/Plugins/PluginSidebarPane.tsx +++ b/browser/src/Plugins/PluginSidebarPane.tsx @@ -18,6 +18,56 @@ import { PluginManager } from "./../Plugins/PluginManager" import { noop } from "./../Utility" +import * as Common from "./../UI/components/common" + +import styled from "styled-components" + +const PluginIconWrapper = styled.div` + background-color: rgba(0, 0, 0, 0.1); + width: 36px; + height: 36px; +` + +const PluginCommandsWrapper = styled.div` + flex: 0 0 auto; +` + +const PluginInfoWrapper = styled.div` + flex: 1 1 auto; + width: 100%; + justify-content: center; + display: flex; + flex-direction: column; + margin-left: 8px; + margin-right: 8px; +` + +const PluginTitleWrapper = styled.div` + font-size: 1.1em; +` + +export interface PluginSidebarItemViewProps { + name: string +} + +export class PluginSidebarItemView extends React.PureComponent { + public render(): JSX.Element { + return ( + + + + + + + + {this.props.name} + + + + ) + } +} + export class PluginsSidebarPane implements SidebarPane { private _onEnter = new Event() private _onLeave = new Event() @@ -62,6 +112,7 @@ export interface IPluginsSidebarPaneViewState { isActive: boolean defaultPluginsExpanded: boolean userPluginsExpanded: boolean + workspacePluginsExpanded: boolean } export class PluginsSidebarPaneView extends React.PureComponent< @@ -77,6 +128,7 @@ export class PluginsSidebarPaneView extends React.PureComponent< isActive: false, defaultPluginsExpanded: false, userPluginsExpanded: true, + workspacePluginsExpanded: false, } } @@ -107,6 +159,7 @@ export class PluginsSidebarPaneView extends React.PureComponent< const allIds = [ "container.default", ...defaultPluginIds, + "container.workspace", "container.user", ...userPluginIds, ] @@ -115,13 +168,14 @@ export class PluginsSidebarPaneView extends React.PureComponent< this._onSelect(id)} render={(selectedId: string) => { const defaultPluginItems = defaultPlugins.map(p => ( } onClick={noop} /> )) @@ -131,7 +185,7 @@ export class PluginsSidebarPaneView extends React.PureComponent< indentationLevel={0} isFocused={p.id === selectedId} isContainer={false} - text={p.id} + text={} onClick={noop} /> )) @@ -139,18 +193,29 @@ export class PluginsSidebarPaneView extends React.PureComponent< return (
this._onSelect("container.default")} > {defaultPluginItems} + this._onSelect("container.workspace")} + > + {[]} + this._onSelect("container.user")} > {userPluginItems} @@ -161,6 +226,29 @@ export class PluginsSidebarPaneView extends React.PureComponent< ) } + private _onSelect(id: string): void { + switch (id) { + case "container.default": + this._toggleDefaultPluginsExpanded() + return + case "container.user": + this._toggleUserPluginsExpanded() + return + } + } + + private _toggleDefaultPluginsExpanded(): void { + this.setState({ + defaultPluginsExpanded: !this.state.defaultPluginsExpanded, + }) + } + + private _toggleUserPluginsExpanded(): void { + this.setState({ + userPluginsExpanded: !this.state.userPluginsExpanded, + }) + } + private _clearExistingSubscriptions(): void { this._subscriptions.forEach(sub => sub.dispose()) this._subscriptions = [] From 1b07ddd7e352f3ce99a541e6bb2ddc1d253b7ada Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Sun, 6 May 2018 13:02:33 -0700 Subject: [PATCH 010/102] Fix #2060 - Improve responsiveness of tutorial on smaller screens (#2170) * WIP: Fix #2060 - remove extra padding and increase minimum height of editor surface * Hide description if the screen is too small --- .../Learning/Tutorial/TutorialBufferLayer.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/browser/src/Services/Learning/Tutorial/TutorialBufferLayer.tsx b/browser/src/Services/Learning/Tutorial/TutorialBufferLayer.tsx index dae057e180..6c56aed169 100644 --- a/browser/src/Services/Learning/Tutorial/TutorialBufferLayer.tsx +++ b/browser/src/Services/Learning/Tutorial/TutorialBufferLayer.tsx @@ -337,13 +337,14 @@ const MainTutorialSectionWrapper = styled.div` flex: 1 1 auto; width: 100%; height: 100%; + min-height: 275px; display: flex; align-items: center; ` const PrimaryHeader = styled.div` - padding-top: 2em; + padding-top: 1em; font-size: 2em; ` @@ -361,6 +362,14 @@ const Section = styled.div` padding-bottom: 2em; ` +const DescriptionWrapper = styled.div` + display: none; + + @media (min-height: 800px) { + display: block; + } +` + export interface IModeStatusBarItemProps { mode: string } @@ -469,7 +478,7 @@ export class TutorialBufferLayerView extends React.PureComponent<
- Description: -
{description}
+ + Description: +
{description}
+
Goals:
{goalsToDisplay}
From 1f6ba60488b192ae7cf02d1dbddedc42c38a6a53 Mon Sep 17 00:00:00 2001 From: Ryan C Date: Sun, 6 May 2018 21:26:44 +0000 Subject: [PATCH 011/102] Add config option for default action of QuickOpen. (#2080) * Add config option for default action of QuickOpen. * Swap to explicitly passing over the file open mode. * Fix default config back to current functionality. --- browser/src/Input/KeyBindings.ts | 2 +- .../Configuration/DefaultConfiguration.ts | 4 ++++ .../Configuration/IConfigurationValues.ts | 2 ++ browser/src/Services/QuickOpen/QuickOpen.ts | 22 ++++++++++++++++++- browser/src/Services/QuickOpen/index.ts | 4 ++-- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/browser/src/Input/KeyBindings.ts b/browser/src/Input/KeyBindings.ts index a41a38451e..f328bc0079 100644 --- a/browser/src/Input/KeyBindings.ts +++ b/browser/src/Input/KeyBindings.ts @@ -101,7 +101,7 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati input.bind([""], "quickOpen.openFileVertical") input.bind([""], "quickOpen.openFileHorizontal") input.bind("", "quickOpen.openFileNewTab") - input.bind([""], "quickOpen.openFileExistingTab") + input.bind([""], "quickOpen.openFileAlternative") // Snippets input.bind("", "snippet.nextPlaceholder") diff --git a/browser/src/Services/Configuration/DefaultConfiguration.ts b/browser/src/Services/Configuration/DefaultConfiguration.ts index f2acbbf3ef..f170c6c36a 100644 --- a/browser/src/Services/Configuration/DefaultConfiguration.ts +++ b/browser/src/Services/Configuration/DefaultConfiguration.ts @@ -6,6 +6,8 @@ import * as os from "os" +import * as Oni from "oni-api" + import * as path from "path" import * as Platform from "./../../Platform" @@ -108,6 +110,8 @@ const BaseConfiguration: IConfigurationValues = { "editor.quickOpen.execCommand": null, "editor.quickOpen.filterStrategy": "vscode", + "editor.quickOpen.defaultOpenMode": Oni.FileOpenMode.Edit, + "editor.quickOpen.alternativeOpenMode": Oni.FileOpenMode.ExistingTab, "editor.split.mode": "native", diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index b515d33454..7ee137cf63 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -131,6 +131,8 @@ export interface IConfigurationValues { "editor.quickInfo.enabled": boolean // Delay (in ms) for showing QuickInfo, when the cursor is on a term "editor.quickInfo.delay": number + "editor.quickOpen.defaultOpenMode": Oni.FileOpenMode + "editor.quickOpen.alternativeOpenMode": Oni.FileOpenMode "editor.errors.slideOnFocus": boolean "editor.formatting.formatOnSwitchToNormalMode": boolean // TODO: Make this setting reliable. If formatting is slow, it will hose edits... not fun diff --git a/browser/src/Services/QuickOpen/QuickOpen.ts b/browser/src/Services/QuickOpen/QuickOpen.ts index fd5895295d..6b2d5e4fe5 100644 --- a/browser/src/Services/QuickOpen/QuickOpen.ts +++ b/browser/src/Services/QuickOpen/QuickOpen.ts @@ -41,7 +41,7 @@ export class QuickOpen { ) { this._menu = menuManager.create() this._menu.onItemSelected.subscribe((selectedItem: any) => { - this._onItemSelected(selectedItem) + this.openFileWithDefaultAction(selectedItem) }) this._menu.onHide.subscribe(() => { @@ -70,6 +70,26 @@ export class QuickOpen { } } + public openFileWithDefaultAction(selectedItem: Oni.Menu.MenuOption): void { + if (!selectedItem) { + return + } + + const defaultOpenMode: Oni.FileOpenMode = configuration.getValue( + "editor.quickOpen.defaultOpenMode", + ) + + this._onItemSelected(selectedItem, defaultOpenMode) + } + + public openFileWithAltAction(): void { + const alternativeOpenMode: Oni.FileOpenMode = configuration.getValue( + "editor.quickOpen.alternativeOpenMode", + ) + + this.openFile(alternativeOpenMode) + } + public async show() { // reset list and show loading indicator this._loadedItems = [] diff --git a/browser/src/Services/QuickOpen/index.ts b/browser/src/Services/QuickOpen/index.ts index b3b5b3796c..fc49985c2a 100644 --- a/browser/src/Services/QuickOpen/index.ts +++ b/browser/src/Services/QuickOpen/index.ts @@ -50,10 +50,10 @@ export const activate = ( }) commandManager.registerCommand({ - command: "quickOpen.openFileExistingTab", + command: "quickOpen.openFileAlternative", name: null, detail: null, - execute: () => _quickOpen.openFile(Oni.FileOpenMode.ExistingTab), + execute: () => _quickOpen.openFileWithAltAction(), enabled: isOpen, }) From 524f9905b7ea4a1327aefbe1eb8d4ad15cce0110 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 8 May 2018 21:04:31 -0700 Subject: [PATCH 012/102] Add Nicolaus Hepler as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 8ef8fe5190..07fae7411e 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -117,6 +117,7 @@ Thanks you to all our backers for making Oni possible! * Jerome Pellois * Wesley Moore * Kim Fiedler +* Nicolaus Hepler From 454634a9b41dc64b72450a7fd995817e525a7c51 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 8 May 2018 21:05:31 -0700 Subject: [PATCH 013/102] Add Nick Price as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 07fae7411e..6bb63e549d 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -118,6 +118,7 @@ Thanks you to all our backers for making Oni possible! * Wesley Moore * Kim Fiedler * Nicolaus Hepler +* Nick Price From 02814ef62a1e9bdf5c9ac32ae38f87858b725f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Griepenburg?= Date: Wed, 9 May 2018 17:15:22 +1000 Subject: [PATCH 014/102] Use yarn to run npm sripts (#2057) (#2177) * Use yarn to run npm sripts (#2057) because for dev setup previously yarn was used to install dependencies while npm was used to run build and setup a symlink to use local oni from shell resulting in issues * Remove global (un)install npm scripts (#2057) as yarn deprecates "install -g" and a global install is not really required due to yarn link --- package.json | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 45f29c8973..9aa68be8a8 100644 --- a/package.json +++ b/package.json @@ -781,43 +781,43 @@ }, "scripts": { "precommit": "pretty-quick --staged", - "prepush": "npm run build && npm run lint", - "build": "npm run build:browser && npm run build:webview_preload && npm run build:main && npm run build:plugins", - "build-debug": "npm run build:browser-debug && npm run build:main && npm run build:plugins", + "prepush": "yarn run build && yarn run lint", + "build": "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins", + "build-debug": "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins", "build:browser": "webpack --config browser/webpack.production.config.js", "build:browser-debug": "webpack --config browser/webpack.debug.config.js", "build:main": "cd main && tsc -p tsconfig.json", - "build:plugins": "npm run build:plugin:oni-plugin-typescript && npm run build:plugin:oni-plugin-markdown-preview", - "build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && npm run build", - "build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && npm run build", - "build:test": "npm run build:test:unit && npm run build:test:integration", + "build:plugins": "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview", + "build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && yarn run build", + "build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn run build", + "build:test": "yarn run build:test:unit && yarn run build:test:integration", "build:test:integration": "cd test && tsc -p tsconfig.json", - "build:test:unit": "npm run build:test:unit:browser && npm run build:test:unit:main", + "build:test:unit": "yarn run build:test:unit:browser && yarn run build:test:unit:main", "build:test:unit:browser": "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", "build:test:unit:main": "rimraf lib_test/main && cd main && tsc -p tsconfig.test.json", "build:webview_preload": "cd webview_preload && tsc -p tsconfig.json", "check-cached-binaries": "node build/script/CheckBinariesForBuild.js", "copy-icons": "node build/CopyIcons.js", "debug:test:unit:browser": "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", - "demo:screenshot": "npm run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", - "demo:video": "npm run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", + "demo:screenshot": "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", + "demo:video": "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", "dist:win:x86": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", "dist:win:x64": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", "pack:win": "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", - "test": "npm run test:unit && npm run test:integration", - "test:integration": "npm run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", + "test": "yarn run test:unit && yarn run test:integration", + "test:integration": "yarn run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", "test:react": "jest --config ./jest.config.js ./ui-tests", "test:react:watch": "jest --config ./jest.config.js ./ui-tests --watch", "test:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage", - "test:unit:browser": "npm run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", - "test:unit": "npm run test:unit:browser && npm run test:unit:main && npm run test:react", - "test:unit:main": "npm run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", + "test:unit:browser": "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "test:unit": "yarn run test:unit:browser && yarn run test:unit:main && yarn run test:react", + "test:unit:main": "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", "upload:dist": "node build/script/UploadDistributionBuildsToAzure", - "fix-lint": "npm run fix-lint:browser && npm run fix-lint:main && npm run fix-lint:test", + "fix-lint": "yarn run fix-lint:browser && yarn run fix-lint:main && yarn run fix-lint:test", "fix-lint:browser": "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "fix-lint:main": "tslint --fix --project main/tsconfig.json --config tslint.json", "fix-lint:test": "tslint --fix --project test/tsconfig.json --config tslint.json", - "lint": "npm run lint:browser && npm run lint:main && npm run lint:test", + "lint": "yarn run lint:browser && yarn run lint:main && yarn run lint:test", "lint:browser": "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "lint:main": "tslint --project main/tsconfig.json --config tslint.json", "lint:test": "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", @@ -829,19 +829,17 @@ "ccov:clean": "rimraf coverage", "ccov:upload": "codecov", "launch": "electron lib/main/src/main.js", - "start": "concurrently --kill-others \"npm run start-hot\" \"npm run watch:browser\" \"npm run watch:plugins\"", + "start": "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"", "start-hot": "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", "start-not-dev": "cross-env electron main.js", "watch:browser": "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", - "watch:plugins": "npm run watch:plugins:oni-plugin-typescript && npm run watch:plugins:oni-plugin-markdown-preview", + "watch:plugins": "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview", "watch:plugins:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && tsc --watch", "watch:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && tsc --watch", - "uninstall-global": "npm rm -g oni-vim", - "install-global": "npm install -g oni-vim", - "install:plugins": "npm run install:plugins:oni-plugin-markdown-preview && npm run install:plugins:oni-plugin-prettier", - "install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && npm install --prod", - "install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && npm install --prod", - "postinstall": "npm run install:plugins && electron-rebuild && opencollective postinstall", + "install:plugins": "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier", + "install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn install --prod", + "install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && yarn install --prod", + "postinstall": "yarn run install:plugins && electron-rebuild && opencollective postinstall", "profile:webpack": "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" }, "repository": { From 75e2b74770bdbcbb884af348b05de19e2f6d45f1 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 9 May 2018 08:22:02 -0700 Subject: [PATCH 015/102] Fix #2047 - Blurry canvas rendering (#2183) * Update canvas element sizing to always be an integer value * Add regression test for canvas sizing * Fix extensions on test file * Turns out offsetWidth/Height is rounded - switch to getBoundingClientRect * Add full suite of CiTests back * Don't use devicePixelRatio in element dimension calculation * Fix lint issue * Fix lint issue --- browser/src/Renderer/CanvasRenderer.ts | 17 +++++----- test/CiTests.ts | 1 + ...gression.2047.VerifyCanvasIsIntegerSize.ts | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 test/ci/Regression.2047.VerifyCanvasIsIntegerSize.ts diff --git a/browser/src/Renderer/CanvasRenderer.ts b/browser/src/Renderer/CanvasRenderer.ts index b6a8687959..0b7d3eb653 100644 --- a/browser/src/Renderer/CanvasRenderer.ts +++ b/browser/src/Renderer/CanvasRenderer.ts @@ -345,19 +345,20 @@ export class CanvasRenderer implements INeovimRenderer { private _setContext(): void { this._editorElement.innerHTML = "" + this._devicePixelRatio = window.devicePixelRatio - this._canvasElement = document.createElement("canvas") - this._canvasElement.style.width = "100%" - this._canvasElement.style.height = "100%" + // offsetWidth and offsetHeight always return an integer + const editorWidth = this._editorElement.offsetWidth + const editorHeight = this._editorElement.offsetHeight - this._devicePixelRatio = window.devicePixelRatio + this._canvasElement = document.createElement("canvas") + this._canvasElement.style.width = editorWidth + "px" + this._canvasElement.style.height = editorHeight + "px" this._editorElement.appendChild(this._canvasElement) - this._width = this._canvasElement.width = - this._canvasElement.offsetWidth * this._devicePixelRatio - this._height = this._canvasElement.height = - this._canvasElement.offsetHeight * this._devicePixelRatio + this._width = this._canvasElement.width = editorWidth * this._devicePixelRatio + this._height = this._canvasElement.height = editorHeight * this._devicePixelRatio if ( configuration.getValue("editor.backgroundImageUrl") && diff --git a/test/CiTests.ts b/test/CiTests.ts index 6ace1b9df0..7c528bd336 100644 --- a/test/CiTests.ts +++ b/test/CiTests.ts @@ -48,6 +48,7 @@ const CiTests = [ "Regression.1296.SettingColorsTest", "Regression.1295.UnfocusedWindowTest", "Regression.1799.MacroApplicationTest", + "Regression.2047.VerifyCanvasIsIntegerSize", "TextmateHighlighting.DebugScopesTest", "TextmateHighlighting.ScopesOnEnterTest", diff --git a/test/ci/Regression.2047.VerifyCanvasIsIntegerSize.ts b/test/ci/Regression.2047.VerifyCanvasIsIntegerSize.ts new file mode 100644 index 0000000000..4788ab6c00 --- /dev/null +++ b/test/ci/Regression.2047.VerifyCanvasIsIntegerSize.ts @@ -0,0 +1,33 @@ +/** + * Regression test for #2047 + */ + +import * as assert from "assert" + +import * as Oni from "oni-api" + +const isInteger = (num: number) => { + return Math.round(num) === num +} + +const assertCanvasIsIntegerSize = (canvasElement: HTMLElement) => { + const rect = canvasElement.getBoundingClientRect() + const measuredWidth = rect.width + const measuredHeight = rect.height + + assert.ok(isInteger(measuredWidth), "Validate the canvas's width is an integer value") + assert.ok(isInteger(measuredHeight), "Validate the canvas's height is an integer value") +} + +export const test = async (oni: Oni.Plugin.Api) => { + await oni.automation.waitForEditors() + + const allCanvasElements = document.getElementsByTagName("canvas") + + assert.ok(allCanvasElements.length > 0, "Verify there is at least one canvas element") + + // tslint:disable-next-line + for (let i = 0; i < allCanvasElements.length; i++) { + assertCanvasIsIntegerSize(allCanvasElements[i]) + } +} From 9260fcc4e7b2ab25012178c70a126494f94ddd44 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Wed, 9 May 2018 20:21:09 +0200 Subject: [PATCH 016/102] Enhance atlas to automatically use multiple textures --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 80 +++++++++++++++--------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index d671917b73..24c1ef2d42 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -1,5 +1,4 @@ -const defaultTextureSizeInPixels = 512 -const glyphPaddingInPixels = 0 +const textureSizeInPixels = 1024 // This should be a safe size for all graphics chips export interface IWebGLAtlasOptions { fontFamily: string @@ -13,6 +12,7 @@ export interface IWebGLAtlasOptions { export interface WebGLGlyph { width: number height: number + textureIndex: number textureWidth: number textureHeight: number textureU: number @@ -24,20 +24,16 @@ export interface WebGLGlyph { export class WebGLAtlas { private _glyphContext: CanvasRenderingContext2D private _glyphs = new Map>() + private _currentTexture: WebGLTexture + private _currentTextureIndex = 0 + private _currentTextureChangedSinceLastUpload = false private _nextX = 0 private _nextY = 0 - private _textureChangedSinceLastUpload = false - private _texture: WebGLTexture - private _textureSize: number - private _uvScale: number constructor(private _gl: WebGL2RenderingContext, private _options: IWebGLAtlasOptions) { - this._textureSize = defaultTextureSizeInPixels * _options.devicePixelRatio - this._uvScale = 1 / this._textureSize - const glyphCanvas = document.createElement("canvas") - glyphCanvas.width = this._textureSize - glyphCanvas.height = this._textureSize + glyphCanvas.width = textureSizeInPixels + glyphCanvas.height = textureSizeInPixels this._glyphContext = glyphCanvas.getContext("2d", { alpha: false }) this._glyphContext.font = `${this._options.fontSize} ${this._options.fontFamily}` this._glyphContext.fillStyle = "white" @@ -45,12 +41,10 @@ export class WebGLAtlas { this._glyphContext.scale(_options.devicePixelRatio, _options.devicePixelRatio) this._glyphContext.imageSmoothingEnabled = false - this._texture = _gl.createTexture() - _gl.bindTexture(_gl.TEXTURE_2D, this._texture) - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.LINEAR) - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE) - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE) - this._textureChangedSinceLastUpload = true + document.body.appendChild(glyphCanvas) + + this._currentTexture = this._gl.createTexture() + this._currentTextureChangedSinceLastUpload = true this.uploadTexture() } @@ -71,24 +65,48 @@ export class WebGLAtlas { } public uploadTexture() { - if (this._textureChangedSinceLastUpload) { + if (this._currentTextureChangedSinceLastUpload) { + this._gl.activeTexture(this._gl.TEXTURE0 + this._currentTextureIndex) + this._gl.bindTexture(this._gl.TEXTURE_2D, this._currentTexture) this._gl.texImage2D( this._gl.TEXTURE_2D, 0, this._gl.RGBA, - this._textureSize, - this._textureSize, + textureSizeInPixels, + textureSizeInPixels, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, this._glyphContext.canvas, ) - this._textureChangedSinceLastUpload = false + this._currentTextureChangedSinceLastUpload = false } } + private _switchToNextTexture() { + if (this._currentTextureIndex >= this._gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) { + throw new Error( + "The WebGL renderer ran out of texture space. Please re-open the editor or switch to a different renderer.", + ) + } + + console.warn("switching to next texture...") + + this._glyphContext.clearRect( + 0, + 0, + this._glyphContext.canvas.width, + this._glyphContext.canvas.width, + ) + this._currentTextureIndex++ + this._currentTexture = this._gl.createTexture() + this._nextX = 0 + this._nextY = 0 + this._currentTextureChangedSinceLastUpload = true + } + private _rasterizeGlyph(text: string, variantIndex: number) { - this._textureChangedSinceLastUpload = true + this._currentTextureChangedSinceLastUpload = true const { devicePixelRatio, @@ -102,14 +120,13 @@ export class WebGLAtlas { const { width: subpixelWidth } = this._glyphContext.measureText(text) const width = Math.ceil(variantOffset) + Math.ceil(subpixelWidth) - if ((this._nextX + width) * devicePixelRatio > this._textureSize) { + if ((this._nextX + width) * devicePixelRatio > textureSizeInPixels) { this._nextX = 0 - this._nextY = Math.ceil(this._nextY + height + glyphPaddingInPixels) + this._nextY = Math.ceil(this._nextY + height) } - if ((this._nextY + height) * devicePixelRatio > this._textureSize) { - // TODO implement a fallback instead of just throwing - throw new Error("Texture is too small") + if ((this._nextY + height) * devicePixelRatio > textureSizeInPixels) { + this._switchToNextTexture() } const x = this._nextX @@ -118,12 +135,13 @@ export class WebGLAtlas { this._nextX += width return { - textureU: x * devicePixelRatio * this._uvScale, - textureV: y * devicePixelRatio * this._uvScale, - textureWidth: width * devicePixelRatio * this._uvScale, - textureHeight: height * devicePixelRatio * this._uvScale, width: width * devicePixelRatio, height: height * devicePixelRatio, + textureIndex: this._currentTextureIndex, + textureU: x * devicePixelRatio / textureSizeInPixels, + textureV: y * devicePixelRatio / textureSizeInPixels, + textureWidth: width * devicePixelRatio / textureSizeInPixels, + textureHeight: height * devicePixelRatio / textureSizeInPixels, subpixelWidth: subpixelWidth * devicePixelRatio, variantOffset, } as WebGLGlyph From c3022e56116cdf0303f4c0ae990c53d5f572a607 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Wed, 9 May 2018 22:21:58 +0200 Subject: [PATCH 017/102] Switch to texture arrays; First working version --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 62 ++++++++++++---- .../src/Renderer/WebGL/WebGLTextRenderer.ts | 70 +++++++++++++++---- 2 files changed, 103 insertions(+), 29 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index 24c1ef2d42..8dd8cf5f65 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -1,4 +1,7 @@ const textureSizeInPixels = 1024 // This should be a safe size for all graphics chips +const maxTextureCount = 8 // TODO possibly use MAX_ARRAY_TEXTURE_LAYERS here +const backgroundColor = "black" +const foregroundColor = "white" export interface IWebGLAtlasOptions { fontFamily: string @@ -24,7 +27,7 @@ export interface WebGLGlyph { export class WebGLAtlas { private _glyphContext: CanvasRenderingContext2D private _glyphs = new Map>() - private _currentTexture: WebGLTexture + private _texture: WebGLTexture private _currentTextureIndex = 0 private _currentTextureChangedSinceLastUpload = false private _nextX = 0 @@ -36,16 +39,43 @@ export class WebGLAtlas { glyphCanvas.height = textureSizeInPixels this._glyphContext = glyphCanvas.getContext("2d", { alpha: false }) this._glyphContext.font = `${this._options.fontSize} ${this._options.fontFamily}` - this._glyphContext.fillStyle = "white" + this._glyphContext.fillStyle = foregroundColor this._glyphContext.textBaseline = "top" this._glyphContext.scale(_options.devicePixelRatio, _options.devicePixelRatio) this._glyphContext.imageSmoothingEnabled = false document.body.appendChild(glyphCanvas) - this._currentTexture = this._gl.createTexture() - this._currentTextureChangedSinceLastUpload = true - this.uploadTexture() + this._texture = this._gl.createTexture() + this._gl.bindTexture(this._gl.TEXTURE_2D_ARRAY, this._texture) + this._gl.texParameteri( + this._gl.TEXTURE_2D_ARRAY, + this._gl.TEXTURE_MIN_FILTER, + this._gl.LINEAR, + ) + this._gl.texParameteri( + this._gl.TEXTURE_2D_ARRAY, + this._gl.TEXTURE_WRAP_S, + this._gl.CLAMP_TO_EDGE, + ) + this._gl.texParameteri( + this._gl.TEXTURE_2D_ARRAY, + this._gl.TEXTURE_WRAP_T, + this._gl.CLAMP_TO_EDGE, + ) + + this._gl.texImage3D( + this._gl.TEXTURE_2D_ARRAY, + 0, + this._gl.RGBA, + textureSizeInPixels, + textureSizeInPixels, + maxTextureCount, + 0, + this._gl.RGBA, + this._gl.UNSIGNED_BYTE, + new Uint8Array(textureSizeInPixels * textureSizeInPixels * maxTextureCount * 4), + ) } public getGlyph(text: string, variantIndex: number) { @@ -66,15 +96,16 @@ export class WebGLAtlas { public uploadTexture() { if (this._currentTextureChangedSinceLastUpload) { - this._gl.activeTexture(this._gl.TEXTURE0 + this._currentTextureIndex) - this._gl.bindTexture(this._gl.TEXTURE_2D, this._currentTexture) - this._gl.texImage2D( - this._gl.TEXTURE_2D, + this._gl.bindTexture(this._gl.TEXTURE_2D_ARRAY, this._texture) + this._gl.texSubImage3D( + this._gl.TEXTURE_2D_ARRAY, 0, - this._gl.RGBA, + 0, + 0, + this._currentTextureIndex, textureSizeInPixels, textureSizeInPixels, - 0, + 1, this._gl.RGBA, this._gl.UNSIGNED_BYTE, this._glyphContext.canvas, @@ -90,16 +121,19 @@ export class WebGLAtlas { ) } - console.warn("switching to next texture...") + console.warn("switching to next texture #", this._currentTextureIndex + 2) + + this.uploadTexture() - this._glyphContext.clearRect( + this._glyphContext.fillStyle = backgroundColor + this._glyphContext.fillRect( 0, 0, this._glyphContext.canvas.width, this._glyphContext.canvas.width, ) + this._glyphContext.fillStyle = foregroundColor this._currentTextureIndex++ - this._currentTexture = this._gl.createTexture() this._nextX = 0 this._nextY = 0 this._currentTextureChangedSinceLastUpload = true diff --git a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts index a3a36124ca..406668be0e 100644 --- a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts @@ -9,7 +9,7 @@ import { // tslint:disable-next-line:no-bitwise const maxGlyphInstances = 1 << 14 // TODO find a reasonable way of determining this -const glyphInstanceFieldCount = 12 +const glyphInstanceFieldCount = 13 const glyphInstanceSizeInBytes = glyphInstanceFieldCount * Float32Array.BYTES_PER_ELEMENT const vertexShaderAttributes = { @@ -17,8 +17,9 @@ const vertexShaderAttributes = { targetOrigin: 1, targetSize: 2, textColorRGBA: 3, - atlasOrigin: 4, - atlasSize: 5, + atlasIndex: 4, + atlasOrigin: 5, + atlasSize: 6, } const vertexShaderSource = ` @@ -28,12 +29,14 @@ const vertexShaderSource = ` layout (location = 1) in vec2 targetOrigin; layout (location = 2) in vec2 targetSize; layout (location = 3) in vec4 textColorRGBA; - layout (location = 4) in vec2 atlasOrigin; - layout (location = 5) in vec2 atlasSize; + layout (location = 4) in float atlasIndex; + layout (location = 5) in vec2 atlasOrigin; + layout (location = 6) in vec2 atlasSize; uniform vec2 viewportScale; flat out vec4 textColor; + flat out int convertedAtlasIndex; out vec2 atlasPosition; void main() { @@ -41,6 +44,7 @@ const vertexShaderSource = ` vec2 targetPosition = targetPixelPosition * viewportScale + vec2(-1.0, 1.0); gl_Position = vec4(targetPosition, 0.0, 1.0); textColor = textColorRGBA; + convertedAtlasIndex = int(atlasIndex); atlasPosition = atlasOrigin + unitQuadVertex * atlasSize; } `.trim() @@ -49,15 +53,17 @@ const firstPassFragmentShaderSource = ` #version 300 es precision mediump float; + precision mediump sampler2DArray; layout(location = 0) out vec4 outColor; flat in vec4 textColor; + flat in int convertedAtlasIndex; in vec2 atlasPosition; - uniform sampler2D atlasTexture; + uniform sampler2DArray atlasTextures; void main() { - vec4 atlasColor = texture(atlasTexture, atlasPosition); + vec4 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasIndex)); outColor = textColor.a * atlasColor; } `.trim() @@ -66,15 +72,17 @@ const secondPassFragmentShaderSource = ` #version 300 es precision mediump float; + precision mediump sampler2DArray; layout(location = 0) out vec4 outColor; flat in vec4 textColor; + flat in int convertedAtlasIndex; in vec2 atlasPosition; - uniform sampler2D atlasTexture; + uniform sampler2DArray atlasTextures; void main() { - vec3 atlasColor = texture(atlasTexture, atlasPosition).rgb; + vec3 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasIndex)).rgb; vec3 outColorRGB = atlasColor * textColor.rgb; float outColorA = max(outColorRGB.r, max(outColorRGB.g, outColorRGB.b)); outColor = vec4(outColorRGB, outColorA); @@ -90,8 +98,10 @@ export class WebGlTextRenderer { private _firstPassProgram: WebGLProgram private _firstPassViewportScaleLocation: WebGLUniformLocation + private _firstPassAtlasTexturesLocation: WebGLUniformLocation private _secondPassProgram: WebGLProgram private _secondPassViewportScaleLocation: WebGLUniformLocation + private _secondPassAtlasTexturesLocation: WebGLUniformLocation private _unitQuadVerticesBuffer: WebGLBuffer private _unitQuadElementIndicesBuffer: WebGLBuffer private _glyphInstances: Float32Array @@ -127,6 +137,15 @@ export class WebGlTextRenderer { "viewportScale", ) + this._firstPassAtlasTexturesLocation = this._gl.getUniformLocation( + this._firstPassProgram, + "atlasTextures", + ) + this._secondPassAtlasTexturesLocation = this._gl.getUniformLocation( + this._secondPassProgram, + "atlasTextures", + ) + this.createBuffers() this.createVertexArrayObject() } @@ -211,6 +230,17 @@ export class WebGlTextRenderer { ) this._gl.vertexAttribDivisor(vertexShaderAttributes.textColorRGBA, 1) + this._gl.enableVertexAttribArray(vertexShaderAttributes.atlasIndex) + this._gl.vertexAttribPointer( + vertexShaderAttributes.atlasIndex, + 1, + this._gl.FLOAT, + false, + glyphInstanceSizeInBytes, + 8 * Float32Array.BYTES_PER_ELEMENT, + ) + this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasIndex, 1) + this._gl.enableVertexAttribArray(vertexShaderAttributes.atlasOrigin) this._gl.vertexAttribPointer( vertexShaderAttributes.atlasOrigin, @@ -218,7 +248,7 @@ export class WebGlTextRenderer { this._gl.FLOAT, false, glyphInstanceSizeInBytes, - 8 * Float32Array.BYTES_PER_ELEMENT, + 9 * Float32Array.BYTES_PER_ELEMENT, ) this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasOrigin, 1) @@ -229,7 +259,7 @@ export class WebGlTextRenderer { this._gl.FLOAT, false, glyphInstanceSizeInBytes, - 10 * Float32Array.BYTES_PER_ELEMENT, + 11 * Float32Array.BYTES_PER_ELEMENT, ) this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasSize, 1) } @@ -287,9 +317,13 @@ export class WebGlTextRenderer { this._gl.enable(this._gl.BLEND) this._gl.useProgram(this._firstPassProgram) + this._gl.uniform2f(this._firstPassViewportScaleLocation, viewportScaleX, viewportScaleY) + this._gl.uniform1i(this._firstPassAtlasTexturesLocation, 0) + this._gl.bindBuffer(this._gl.ARRAY_BUFFER, this._glyphInstancesBuffer) this._gl.bufferData(this._gl.ARRAY_BUFFER, this._glyphInstances, this._gl.STREAM_DRAW) + this._gl.blendFuncSeparate( this._gl.ZERO, this._gl.ONE_MINUS_SRC_COLOR, @@ -299,13 +333,17 @@ export class WebGlTextRenderer { this._gl.drawElementsInstanced(this._gl.TRIANGLES, 6, this._gl.UNSIGNED_BYTE, 0, glyphCount) this._gl.useProgram(this._secondPassProgram) + this._gl.blendFuncSeparate( this._gl.ONE, this._gl.ONE, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA, ) + this._gl.uniform2f(this._secondPassViewportScaleLocation, viewportScaleX, viewportScaleY) + this._gl.uniform1i(this._secondPassAtlasTexturesLocation, 0) + this._gl.drawElementsInstanced(this._gl.TRIANGLES, 6, this._gl.UNSIGNED_BYTE, 0, glyphCount) } @@ -328,11 +366,13 @@ export class WebGlTextRenderer { this._glyphInstances[5 + startOffset] = color[1] this._glyphInstances[6 + startOffset] = color[2] this._glyphInstances[7 + startOffset] = color[3] + // atlasIndex + this._glyphInstances[8 + startOffset] = glyph.textureIndex // atlasOrigin - this._glyphInstances[8 + startOffset] = glyph.textureU - this._glyphInstances[9 + startOffset] = glyph.textureV + this._glyphInstances[9 + startOffset] = glyph.textureU + this._glyphInstances[10 + startOffset] = glyph.textureV // atlasSize - this._glyphInstances[10 + startOffset] = glyph.textureWidth - this._glyphInstances[11 + startOffset] = glyph.textureHeight + this._glyphInstances[11 + startOffset] = glyph.textureWidth + this._glyphInstances[12 + startOffset] = glyph.textureHeight } } From c6473bcb360da2667948728956c6e8ea41608cc4 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Wed, 9 May 2018 22:25:39 +0200 Subject: [PATCH 018/102] Remove console.warn --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index 8dd8cf5f65..55026c840e 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -121,8 +121,6 @@ export class WebGLAtlas { ) } - console.warn("switching to next texture #", this._currentTextureIndex + 2) - this.uploadTexture() this._glyphContext.fillStyle = backgroundColor From 517280af3a1b30df84bec8f643bd20b111a7312c Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 11 May 2018 08:27:22 -0700 Subject: [PATCH 019/102] Feature: Sneak tags for automation (#2187) * Factor out the sneak-tagging functionality from #2019 * Fix lint issues * Bring over id --- browser/src/Plugins/Api/Oni.ts | 5 +++++ .../src/Services/Browser/AddressBarView.tsx | 2 +- browser/src/Services/Sidebar/SidebarView.tsx | 4 +++- browser/src/Services/Sneak/Sneak.tsx | 19 ++++++++++++++++++- browser/src/Services/Sneak/SneakStore.ts | 3 +++ browser/src/UI/components/Sneakable.tsx | 2 ++ 6 files changed, 32 insertions(+), 3 deletions(-) diff --git a/browser/src/Plugins/Api/Oni.ts b/browser/src/Plugins/Api/Oni.ts index 989dee8a16..37e2f63792 100644 --- a/browser/src/Plugins/Api/Oni.ts +++ b/browser/src/Plugins/Api/Oni.ts @@ -31,6 +31,7 @@ import { getInstance as getNotificationsInstance } from "./../../Services/Notifi import { getInstance as getOverlayInstance } from "./../../Services/Overlay" import { recorder } from "./../../Services/Recorder" import { getInstance as getSidebarInstance } from "./../../Services/Sidebar" +import { getInstance as getSneakInstance } from "./../../Services/Sneak" import { getInstance as getSnippetsInstance } from "./../../Services/Snippets" import { getInstance as getStatusBarInstance } from "./../../Services/StatusBar" import { getInstance as getTokenColorsInstance } from "./../../Services/TokenColors" @@ -141,6 +142,10 @@ export class Oni implements OniApi.Plugin.Api { return getSidebarInstance() } + public get sneak(): any { + return getSneakInstance() + } + public get snippets(): OniApi.Snippets.SnippetManager { return getSnippetsInstance() } diff --git a/browser/src/Services/Browser/AddressBarView.tsx b/browser/src/Services/Browser/AddressBarView.tsx index c1b0cd3331..283be95240 100644 --- a/browser/src/Services/Browser/AddressBarView.tsx +++ b/browser/src/Services/Browser/AddressBarView.tsx @@ -80,7 +80,7 @@ export class AddressBarView extends React.PureComponent< private _renderAddressSpan(): JSX.Element { return ( - this._setActive()}> + this._setActive()} tag={"browser.address"}> this._setActive()}>{this.props.url} ) diff --git a/browser/src/Services/Sidebar/SidebarView.tsx b/browser/src/Services/Sidebar/SidebarView.tsx index c43c92feed..d5fae42a90 100644 --- a/browser/src/Services/Sidebar/SidebarView.tsx +++ b/browser/src/Services/Sidebar/SidebarView.tsx @@ -17,6 +17,7 @@ import { withProps } from "./../../UI/components/common" import { Sneakable } from "./../../UI/components/Sneakable" export interface ISidebarIconProps { + id: string active: boolean focused: boolean iconName: string @@ -90,7 +91,7 @@ export class SidebarIcon extends React.PureComponent { public render(): JSX.Element { const notification = this.props.hasNotification ? : null return ( - + @@ -154,6 +155,7 @@ export class SidebarView extends React.PureComponent { const isFocused = e.id === selectedId && this.props.isActive return ( Promise @@ -41,6 +41,23 @@ export class Sneak { return { dispose } } + // Get the first sneak with a 'tag' matching the passed in tag + public getSneakMatchingTag(tag: string): IAugmentedSneakInfo | null { + + if (!this.isActive) { + return null + } + + const sneaks = this._store.getState().sneaks + + if (sneaks || sneaks.length === 0) { + return null + } + + return sneaks.find(s => s.tag && s.tag === tag) + + } + public show(): void { if (this._activeOverlay) { this._activeOverlay.hide() diff --git a/browser/src/Services/Sneak/SneakStore.ts b/browser/src/Services/Sneak/SneakStore.ts index 9d4370c561..beea70b835 100644 --- a/browser/src/Services/Sneak/SneakStore.ts +++ b/browser/src/Services/Sneak/SneakStore.ts @@ -13,6 +13,9 @@ import { createStore as createReduxStore } from "./../../Redux" export interface ISneakInfo { rectangle: Shapes.Rectangle callback: () => void + + // `tag` is an optional string used to identify the sneak + tag?: string } export interface IAugmentedSneakInfo extends ISneakInfo { diff --git a/browser/src/UI/components/Sneakable.tsx b/browser/src/UI/components/Sneakable.tsx index c342cce1e3..21d7711c8f 100644 --- a/browser/src/UI/components/Sneakable.tsx +++ b/browser/src/UI/components/Sneakable.tsx @@ -14,6 +14,7 @@ import { /* SneakProvider, Sneak,*/ getInstance as getSneak } from "./../../Serv import { EmptyArray } from "./../../Utility" export interface ISneakableProps { + tag?: string callback?: (evt?: any) => void } @@ -39,6 +40,7 @@ export class Sneakable extends React.PureComponent { rect.width, rect.height, ), + tag: this.props.tag || null, }, ] } else { From c1a79cdd360bf8db86cb240ef754174c3cd46f50 Mon Sep 17 00:00:00 2001 From: Kazuyuki Kamata Date: Sat, 12 May 2018 03:32:35 +0900 Subject: [PATCH 020/102] Fix regression in editor.quickInfo.show functionality (#2193) --- .../src/Editor/NeovimEditor/NeovimEditor.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index 617a9eca76..2db70bd8d9 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -307,16 +307,6 @@ export class NeovimEditor extends Editor implements IEditor { ) // Services - this._commands = new NeovimEditorCommands( - commandManager, - this._contextMenuManager, - this._definition, - this._languageIntegration, - this._neovimInstance, - this._rename, - this._symbols, - ) - const onColorsChanged = () => { const updatedColors: any = this._colors.getColors() this._actions.setColors(updatedColors) @@ -723,6 +713,16 @@ export class NeovimEditor extends Editor implements IEditor { }), ) + this._commands = new NeovimEditorCommands( + commandManager, + this._contextMenuManager, + this._definition, + this._languageIntegration, + this._neovimInstance, + this._rename, + this._symbols, + ) + this._render() this._onConfigChanged(this._configuration.getValues()) From c930141f7e76065e3f0ccde907bb76ac78a7f80a Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 11 May 2018 16:04:46 -0700 Subject: [PATCH 021/102] Add `--plugin-develop` flag (#2189) * Add --plugin-develop flag * Clean up code --- browser/src/App.ts | 7 +++++++ browser/src/Plugins/PluginManager.ts | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/browser/src/App.ts b/browser/src/App.ts index 336e137b8b..3e1b8cf0cf 100644 --- a/browser/src/App.ts +++ b/browser/src/App.ts @@ -133,6 +133,13 @@ export const start = async (args: string[]): Promise => { PluginManager.activate(configuration) const pluginManager = PluginManager.getInstance() + const developmentPlugin = parsedArgs["plugin-develop"] + + if (developmentPlugin) { + Log.info("Registering development plugin: " + developmentPlugin) + pluginManager.addDevelopmentPlugin(developmentPlugin) + } + Performance.startMeasure("Oni.Start.Plugins.Discover") pluginManager.discoverPlugins() Performance.endMeasure("Oni.Start.Plugins.Discover") diff --git a/browser/src/Plugins/PluginManager.ts b/browser/src/Plugins/PluginManager.ts index 3c151f7123..4c0c19b32d 100644 --- a/browser/src/Plugins/PluginManager.ts +++ b/browser/src/Plugins/PluginManager.ts @@ -24,6 +24,8 @@ export class PluginManager implements Oni.IPluginManager { private _pluginsActivated: boolean = false private _installer: IPluginInstaller = new YarnPluginInstaller() + private _developmentPluginsPath: string[] = [] + public get plugins(): Plugin[] { return this._plugins } @@ -34,6 +36,10 @@ export class PluginManager implements Oni.IPluginManager { constructor(private _config: Configuration) {} + public addDevelopmentPlugin(pluginPath: string): void { + this._developmentPluginsPath.push(pluginPath) + } + public discoverPlugins(): void { const corePluginRootPaths: string[] = [corePluginsRoot, extensionsRoot] const corePlugins: Plugin[] = this._getAllPluginPaths(corePluginRootPaths).map(p => @@ -55,12 +61,14 @@ export class PluginManager implements Oni.IPluginManager { this._createPlugin(p, "user"), ) + const developmentPlugins = this._developmentPluginsPath.map((dev) => this._createPlugin(dev, "development")) + this._rootPluginPaths = [ ...corePluginRootPaths, ...defaultPluginRootPaths, ...userPluginsRootPath, ] - this._plugins = [...corePlugins, ...defaultPlugins, ...userPlugins] + this._plugins = [...corePlugins, ...defaultPlugins, ...userPlugins, ...developmentPlugins] this._anonymousPlugin = new AnonymousPlugin() } @@ -76,7 +84,7 @@ export class PluginManager implements Oni.IPluginManager { } public getAllRuntimePaths(): string[] { - const pluginPaths = this._getAllPluginPaths(this._rootPluginPaths) + const pluginPaths = [...this._getAllPluginPaths(this._rootPluginPaths), ...this._developmentPluginsPath] return pluginPaths.concat(this._rootPluginPaths) } From 035bad1b276fdd4c16e06571eec176add615f875 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 11 May 2018 17:06:07 -0700 Subject: [PATCH 022/102] Fix #2191 - Snippet loading issues (#2196) * Refactor code that parses the string from the code that loads the file for testability * Add test case to exercise the working case * Add failing test case for single-line body * Get failing test green * Add failing test case for trailing comma case * Get failing test green by using a more forgiving JSON parsing library * Fix lint issues --- .../src/Services/Snippets/SnippetProvider.ts | 32 +++++--- browser/src/Utility.ts | 5 ++ .../Services/Snippets/SnippetProviderTests.ts | 73 +++++++++++++++++++ package.json | 2 + yarn.lock | 10 +++ 5 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 browser/test/Services/Snippets/SnippetProviderTests.ts diff --git a/browser/src/Services/Snippets/SnippetProvider.ts b/browser/src/Services/Snippets/SnippetProvider.ts index a42ea28e55..ae57baa54e 100644 --- a/browser/src/Services/Snippets/SnippetProvider.ts +++ b/browser/src/Services/Snippets/SnippetProvider.ts @@ -14,7 +14,7 @@ import { PluginManager } from "./../../Plugins/PluginManager" import { Configuration } from "./../Configuration" import * as Log from "./../../Log" -import { flatMap } from "./../../Utility" +import * as Utility from "./../../Utility" export class CompositeSnippetProvider implements Oni.Snippets.SnippetProvider { private _providers: Oni.Snippets.SnippetProvider[] = [] @@ -64,9 +64,10 @@ export class PluginSnippetProvider implements Oni.Snippets.SnippetProvider { p => p.metadata && p.metadata.contributes && p.metadata.contributes.snippets, ) - const snippets = flatMap(filteredPlugins, pc => pc.metadata.contributes.snippets).filter( - s => s.language === language, - ) + const snippets = Utility.flatMap( + filteredPlugins, + pc => pc.metadata.contributes.snippets, + ).filter(s => s.language === language) const snippetLoadPromises = snippets.map(s => this._loadSnippetsFromFile(s.path)) const loadedSnippets = await Promise.all(snippetLoadPromises) @@ -99,24 +100,35 @@ export const loadSnippetsFromFile = async ( }) }) + const snippets = loadSnippetsFromText(contents) + + Log.verbose( + `[loadSnippetsFromFile] - Loaded ${snippets.length} snippets from ${snippetFilePath}`, + ) + + return snippets +} + +interface KeyToSnippet { + [key: string]: ISnippetPluginContribution +} + +export const loadSnippetsFromText = (contents: string): Oni.Snippets.Snippet[] => { let snippets: ISnippetPluginContribution[] = [] try { - snippets = Object.values(JSON.parse(contents)) as ISnippetPluginContribution[] + const snippetObject = Utility.parseJson5(contents) + snippets = Object.values(snippetObject) } catch (ex) { Log.error(ex) snippets = [] } - Log.verbose( - `[loadSnippetsFromFile] - Loaded ${snippets.length} snippets from ${snippetFilePath}`, - ) - const normalizedSnippets = snippets.map( (snip: ISnippetPluginContribution): Oni.Snippets.Snippet => { return { prefix: snip.prefix, description: snip.description, - body: snip.body.join(os.EOL), + body: typeof snip.body === "string" ? snip.body : snip.body.join(os.EOL), } }, ) diff --git a/browser/src/Utility.ts b/browser/src/Utility.ts index c496436b87..92a177083f 100644 --- a/browser/src/Utility.ts +++ b/browser/src/Utility.ts @@ -15,6 +15,7 @@ import * as reduce from "lodash/reduce" import { Observable } from "rxjs/Observable" import { Subject } from "rxjs/Subject" +import * as JSON5 from "json5" import { IDisposable } from "oni-types" import * as types from "vscode-languageserver-types" @@ -245,3 +246,7 @@ export function ignoreWhilePendingPromise( return ret } + +export const parseJson5 = (text: string): T => { + return JSON5.parse(text) as T +} diff --git a/browser/test/Services/Snippets/SnippetProviderTests.ts b/browser/test/Services/Snippets/SnippetProviderTests.ts new file mode 100644 index 0000000000..925fd3be63 --- /dev/null +++ b/browser/test/Services/Snippets/SnippetProviderTests.ts @@ -0,0 +1,73 @@ +/** + * SnippetProviderTests.ts + */ + +import * as assert from "assert" +import * as os from "os" + +import { loadSnippetsFromText } from "./../../../src/Services/Snippets/SnippetProvider" + +const ArraySnippet = ` +{ + "if": { + "prefix": "test", + "body": [ + "line1", + "line2" + ], + "description": "Code snippet for an if statement" + } +} +` + +const SingleLineSnippet = ` +{ + "if": { + "prefix": "test", + "body": "line1", + "description": "Code snippet for an if statement" + } +} +` + +const TrailingCommaSnippet = ` +{ + "if": { + "prefix": "test", + "body": ["line1"], + "description": "Code snippet for an if statement" + }, +} +` + +describe("SnippetProviderTests", () => { + describe("loadSnippetsFromText", () => { + it("parses a basic snippet", async () => { + const [parsedArraySnippet] = loadSnippetsFromText(ArraySnippet) + + assert.strictEqual( + parsedArraySnippet.body, + "line1" + os.EOL + "line2", + "Validate body was parsed correctly", + ) + }) + + it("parses single-line snippet", async () => { + const [parsedSingleLineSnippet] = loadSnippetsFromText(SingleLineSnippet) + assert.strictEqual( + parsedSingleLineSnippet.body, + "line1", + "Validate body was parsed correctly", + ) + }) + + it("parses snippet with trailing comma", async () => { + const [parsedTrailingCommaSnippet] = loadSnippetsFromText(TrailingCommaSnippet) + assert.strictEqual( + parsedTrailingCommaSnippet.body, + "line1", + "Validate body was parsed correctly", + ) + }) + }) +}) diff --git a/package.json b/package.json index 9aa68be8a8..4d55df213f 100644 --- a/package.json +++ b/package.json @@ -854,6 +854,7 @@ "electron-settings": "^3.1.4", "find-up": "2.1.0", "fs-extra": "^5.0.0", + "json5": "^1.0.1", "keyboard-layout": "^2.0.13", "marked": "^0.3.6", "minimist": "1.2.0", @@ -890,6 +891,7 @@ "@types/fs-extra": "^5.0.2", "@types/jest": "^22.1.3", "@types/jsdom": "11.0.0", + "@types/json5": "^0.0.29", "@types/lodash": "4.14.38", "@types/lolex": "2.1.0", "@types/marked": "^0.3.0", diff --git a/yarn.lock b/yarn.lock index c73f7fb56a..dafabd626c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -126,6 +126,10 @@ "@types/tough-cookie" "*" parse5 "^3.0.2" +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + "@types/lodash@4.14.38": version "4.14.38" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.38.tgz#fcbd51e740c5e260652cad975dd26c5c4151cb11" @@ -6083,6 +6087,12 @@ json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" From 6073f51ed0180db9d1bd002bcfb5445b6fe96327 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 11 May 2018 19:30:47 -0700 Subject: [PATCH 023/102] Performance: Add selector for NeovimBufferLayerView (#2195) * Add selector to NeovimBufferLayers for performance * Fix lint issues --- .../NeovimEditor/NeovimBufferLayersView.tsx | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx b/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx index 5bdd719038..3f49d3d24f 100644 --- a/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx @@ -6,6 +6,7 @@ import * as React from "react" import { connect } from "react-redux" +import { createSelector } from "reselect" import * as Oni from "oni-api" @@ -118,20 +119,30 @@ const EmptyState: NeovimBufferLayersViewProps = { fontPixelWidth: -1, } +const getActiveVimTabPage = (state: State.IState) => state.activeVimTabPage +const getWindowState = (state: State.IState) => state.windowState + +const windowSelector = createSelector( + [getActiveVimTabPage, getWindowState], + (tabPage: State.IVimTabPage, windowState: State.IWindowState) => { + const windows = tabPage.windowIds.map(windowId => { + return windowState.windows[windowId] + }) + + return windows.sort((a, b) => a.windowId - b.windowId) + }, +) + const mapStateToProps = (state: State.IState): NeovimBufferLayersViewProps => { if (!state.activeVimTabPage) { return EmptyState } - const windows = state.activeVimTabPage.windowIds.map(windowId => { - return state.windowState.windows[windowId] - }) - - const wins = windows.sort((a, b) => a.windowId - b.windowId) + const windows = windowSelector(state) return { activeWindowId: state.windowState.activeWindow, - windows: wins, + windows, layers: state.layers, fontPixelWidth: state.fontPixelWidth, fontPixelHeight: state.fontPixelHeight, From 7b3163295c39d4f6473b8030032f7560624b241c Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sun, 13 May 2018 09:51:17 +0200 Subject: [PATCH 024/102] Add an exponential increase of the texture layers when the space is exceeded and fix the undefined error --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 101 +++++++++++--------- browser/src/Renderer/WebGL/WebGLRenderer.ts | 45 ++++++--- 2 files changed, 86 insertions(+), 60 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index 55026c840e..d96ac4dbb0 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -1,5 +1,3 @@ -const textureSizeInPixels = 1024 // This should be a safe size for all graphics chips -const maxTextureCount = 8 // TODO possibly use MAX_ARRAY_TEXTURE_LAYERS here const backgroundColor = "black" const foregroundColor = "white" @@ -10,6 +8,8 @@ export interface IWebGLAtlasOptions { linePaddingInPixels: number devicePixelRatio: number offsetGlyphVariantCount: number + textureSizeInPixels: number + textureLayerCount: number } export interface WebGLGlyph { @@ -24,19 +24,21 @@ export interface WebGLGlyph { subpixelWidth: number } +export class WebGLTextureSpaceExceededError extends Error {} + export class WebGLAtlas { private _glyphContext: CanvasRenderingContext2D private _glyphs = new Map>() private _texture: WebGLTexture - private _currentTextureIndex = 0 - private _currentTextureChangedSinceLastUpload = false + private _currentTextureLayerIndex = 0 + private _currentTextureLayerChangedSinceLastUpload = false private _nextX = 0 private _nextY = 0 constructor(private _gl: WebGL2RenderingContext, private _options: IWebGLAtlasOptions) { const glyphCanvas = document.createElement("canvas") - glyphCanvas.width = textureSizeInPixels - glyphCanvas.height = textureSizeInPixels + glyphCanvas.width = this._options.textureSizeInPixels + glyphCanvas.height = this._options.textureSizeInPixels this._glyphContext = glyphCanvas.getContext("2d", { alpha: false }) this._glyphContext.font = `${this._options.fontSize} ${this._options.fontFamily}` this._glyphContext.fillStyle = foregroundColor @@ -64,17 +66,21 @@ export class WebGLAtlas { this._gl.CLAMP_TO_EDGE, ) + const textureLayerCount = Math.min( + this._options.textureLayerCount, + this._gl.MAX_ARRAY_TEXTURE_LAYERS, + ) this._gl.texImage3D( this._gl.TEXTURE_2D_ARRAY, 0, this._gl.RGBA, - textureSizeInPixels, - textureSizeInPixels, - maxTextureCount, + this._options.textureSizeInPixels, + this._options.textureSizeInPixels, + textureLayerCount, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, - new Uint8Array(textureSizeInPixels * textureSizeInPixels * maxTextureCount * 4), + null, ) } @@ -95,50 +101,27 @@ export class WebGLAtlas { } public uploadTexture() { - if (this._currentTextureChangedSinceLastUpload) { + if (this._currentTextureLayerChangedSinceLastUpload) { this._gl.bindTexture(this._gl.TEXTURE_2D_ARRAY, this._texture) this._gl.texSubImage3D( this._gl.TEXTURE_2D_ARRAY, 0, 0, 0, - this._currentTextureIndex, - textureSizeInPixels, - textureSizeInPixels, + this._currentTextureLayerIndex, + this._options.textureSizeInPixels, + this._options.textureSizeInPixels, 1, this._gl.RGBA, this._gl.UNSIGNED_BYTE, this._glyphContext.canvas, ) - this._currentTextureChangedSinceLastUpload = false + this._currentTextureLayerChangedSinceLastUpload = false } } - private _switchToNextTexture() { - if (this._currentTextureIndex >= this._gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) { - throw new Error( - "The WebGL renderer ran out of texture space. Please re-open the editor or switch to a different renderer.", - ) - } - - this.uploadTexture() - - this._glyphContext.fillStyle = backgroundColor - this._glyphContext.fillRect( - 0, - 0, - this._glyphContext.canvas.width, - this._glyphContext.canvas.width, - ) - this._glyphContext.fillStyle = foregroundColor - this._currentTextureIndex++ - this._nextX = 0 - this._nextY = 0 - this._currentTextureChangedSinceLastUpload = true - } - private _rasterizeGlyph(text: string, variantIndex: number) { - this._currentTextureChangedSinceLastUpload = true + this._currentTextureLayerChangedSinceLastUpload = true const { devicePixelRatio, @@ -152,13 +135,13 @@ export class WebGLAtlas { const { width: subpixelWidth } = this._glyphContext.measureText(text) const width = Math.ceil(variantOffset) + Math.ceil(subpixelWidth) - if ((this._nextX + width) * devicePixelRatio > textureSizeInPixels) { + if ((this._nextX + width) * devicePixelRatio > this._options.textureSizeInPixels) { this._nextX = 0 this._nextY = Math.ceil(this._nextY + height) } - if ((this._nextY + height) * devicePixelRatio > textureSizeInPixels) { - this._switchToNextTexture() + if ((this._nextY + height) * devicePixelRatio > this._options.textureSizeInPixels) { + this._switchToNextLayer() } const x = this._nextX @@ -169,13 +152,37 @@ export class WebGLAtlas { return { width: width * devicePixelRatio, height: height * devicePixelRatio, - textureIndex: this._currentTextureIndex, - textureU: x * devicePixelRatio / textureSizeInPixels, - textureV: y * devicePixelRatio / textureSizeInPixels, - textureWidth: width * devicePixelRatio / textureSizeInPixels, - textureHeight: height * devicePixelRatio / textureSizeInPixels, + textureIndex: this._currentTextureLayerIndex, + textureU: x * devicePixelRatio / this._options.textureSizeInPixels, + textureV: y * devicePixelRatio / this._options.textureSizeInPixels, + textureWidth: width * devicePixelRatio / this._options.textureSizeInPixels, + textureHeight: height * devicePixelRatio / this._options.textureSizeInPixels, subpixelWidth: subpixelWidth * devicePixelRatio, variantOffset, } as WebGLGlyph } + + private _switchToNextLayer() { + if (this._currentTextureLayerIndex + 1 >= this._options.textureLayerCount) { + throw new WebGLTextureSpaceExceededError( + "The WebGL renderer ran out of texture space. Please re-open the editor " + + "with more texture layers or switch to a different renderer.", + ) + } + + this.uploadTexture() + + this._glyphContext.fillStyle = backgroundColor + this._glyphContext.fillRect( + 0, + 0, + this._glyphContext.canvas.width, + this._glyphContext.canvas.width, + ) + this._glyphContext.fillStyle = foregroundColor + this._currentTextureLayerIndex++ + this._nextX = 0 + this._nextY = 0 + this._currentTextureLayerChangedSinceLastUpload = true + } } diff --git a/browser/src/Renderer/WebGL/WebGLRenderer.ts b/browser/src/Renderer/WebGL/WebGLRenderer.ts index b7816bb346..6415ee6b90 100644 --- a/browser/src/Renderer/WebGL/WebGLRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLRenderer.ts @@ -2,7 +2,7 @@ import { INeovimRenderer } from ".." import { IScreen } from "../../neovim" import { CachedColorNormalizer } from "./CachedColorNormalizer" import { IColorNormalizer } from "./IColorNormalizer" -import { IWebGLAtlasOptions } from "./WebGLAtlas" +import { IWebGLAtlasOptions, WebGLTextureSpaceExceededError } from "./WebGLAtlas" import { WebGLSolidRenderer } from "./WebGLSolidRenderer" import { WebGlTextRenderer } from "./WebGLTextRenderer" @@ -10,6 +10,8 @@ export class WebGLRenderer implements INeovimRenderer { private _editorElement: HTMLElement private _colorNormalizer: IColorNormalizer private _previousAtlasOptions: IWebGLAtlasOptions + private _textureSizeInPixels = 1024 + private _textureLayerCount = 2 private _gl: WebGL2RenderingContext private _solidRenderer: WebGLSolidRenderer @@ -18,22 +20,24 @@ export class WebGLRenderer implements INeovimRenderer { public start(editorElement: HTMLElement): void { this._editorElement = editorElement this._colorNormalizer = new CachedColorNormalizer() - - const canvasElement = document.createElement("canvas") - canvasElement.style.width = `100%` - canvasElement.style.height = `100%` - - this._editorElement.innerHTML = "" - this._editorElement.appendChild(canvasElement) - - this._gl = canvasElement.getContext("webgl2") as WebGL2RenderingContext } public redrawAll(screenInfo: IScreen): void { + this._initializeCanvasIfRequired() this._updateCanvasDimensions() this._createNewRendererIfRequired(screenInfo) this._clear(screenInfo.backgroundColor) - this._draw(screenInfo) + + try { + this._draw(screenInfo) + } catch (error) { + if (error instanceof WebGLTextureSpaceExceededError) { + this._textureLayerCount *= 2 + this.redrawAll(screenInfo) + } else { + throw error + } + } } public draw(screenInfo: IScreen): void { @@ -44,10 +48,23 @@ export class WebGLRenderer implements INeovimRenderer { // do nothing } + private _initializeCanvasIfRequired() { + if (!this._gl) { + const canvasElement = document.createElement("canvas") + this._editorElement.innerHTML = "" + this._editorElement.appendChild(canvasElement) + + this._gl = canvasElement.getContext("webgl2") as WebGL2RenderingContext + } + } + private _updateCanvasDimensions() { const devicePixelRatio = window.devicePixelRatio - this._gl.canvas.width = this._editorElement.offsetWidth * devicePixelRatio - this._gl.canvas.height = this._editorElement.offsetHeight * devicePixelRatio + const canvas = this._gl.canvas + canvas.width = this._editorElement.offsetWidth * devicePixelRatio + canvas.height = this._editorElement.offsetHeight * devicePixelRatio + canvas.style.width = `${canvas.width / devicePixelRatio}px` + canvas.style.height = `${canvas.height / devicePixelRatio}px` } private _createNewRendererIfRequired({ @@ -68,6 +85,8 @@ export class WebGLRenderer implements INeovimRenderer { linePaddingInPixels, devicePixelRatio, offsetGlyphVariantCount, + textureSizeInPixels: this._textureSizeInPixels, + textureLayerCount: this._textureLayerCount, } as IWebGLAtlasOptions if ( From 3e1d9587c7d8e3ac3329d6e783c132921666b4e4 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sun, 13 May 2018 10:11:21 +0200 Subject: [PATCH 025/102] Align naming of the atlasLayerIndex --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 4 +-- .../src/Renderer/WebGL/WebGLTextRenderer.ts | 26 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index d96ac4dbb0..dd2f0667e9 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -15,7 +15,7 @@ export interface IWebGLAtlasOptions { export interface WebGLGlyph { width: number height: number - textureIndex: number + textureLayerIndex: number textureWidth: number textureHeight: number textureU: number @@ -152,7 +152,7 @@ export class WebGLAtlas { return { width: width * devicePixelRatio, height: height * devicePixelRatio, - textureIndex: this._currentTextureLayerIndex, + textureLayerIndex: this._currentTextureLayerIndex, textureU: x * devicePixelRatio / this._options.textureSizeInPixels, textureV: y * devicePixelRatio / this._options.textureSizeInPixels, textureWidth: width * devicePixelRatio / this._options.textureSizeInPixels, diff --git a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts index 406668be0e..735f3e359f 100644 --- a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts @@ -17,7 +17,7 @@ const vertexShaderAttributes = { targetOrigin: 1, targetSize: 2, textColorRGBA: 3, - atlasIndex: 4, + atlasLayerIndex: 4, atlasOrigin: 5, atlasSize: 6, } @@ -29,14 +29,14 @@ const vertexShaderSource = ` layout (location = 1) in vec2 targetOrigin; layout (location = 2) in vec2 targetSize; layout (location = 3) in vec4 textColorRGBA; - layout (location = 4) in float atlasIndex; + layout (location = 4) in float atlasLayerIndex; layout (location = 5) in vec2 atlasOrigin; layout (location = 6) in vec2 atlasSize; uniform vec2 viewportScale; flat out vec4 textColor; - flat out int convertedAtlasIndex; + flat out int convertedAtlasLayerIndex; out vec2 atlasPosition; void main() { @@ -44,7 +44,7 @@ const vertexShaderSource = ` vec2 targetPosition = targetPixelPosition * viewportScale + vec2(-1.0, 1.0); gl_Position = vec4(targetPosition, 0.0, 1.0); textColor = textColorRGBA; - convertedAtlasIndex = int(atlasIndex); + convertedAtlasLayerIndex = int(atlasLayerIndex); atlasPosition = atlasOrigin + unitQuadVertex * atlasSize; } `.trim() @@ -57,13 +57,13 @@ const firstPassFragmentShaderSource = ` layout(location = 0) out vec4 outColor; flat in vec4 textColor; - flat in int convertedAtlasIndex; + flat in int convertedAtlasLayerIndex; in vec2 atlasPosition; uniform sampler2DArray atlasTextures; void main() { - vec4 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasIndex)); + vec4 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasLayerIndex)); outColor = textColor.a * atlasColor; } `.trim() @@ -76,13 +76,13 @@ const secondPassFragmentShaderSource = ` layout(location = 0) out vec4 outColor; flat in vec4 textColor; - flat in int convertedAtlasIndex; + flat in int convertedAtlasLayerIndex; in vec2 atlasPosition; uniform sampler2DArray atlasTextures; void main() { - vec3 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasIndex)).rgb; + vec3 atlasColor = texture(atlasTextures, vec3(atlasPosition, convertedAtlasLayerIndex)).rgb; vec3 outColorRGB = atlasColor * textColor.rgb; float outColorA = max(outColorRGB.r, max(outColorRGB.g, outColorRGB.b)); outColor = vec4(outColorRGB, outColorA); @@ -230,16 +230,16 @@ export class WebGlTextRenderer { ) this._gl.vertexAttribDivisor(vertexShaderAttributes.textColorRGBA, 1) - this._gl.enableVertexAttribArray(vertexShaderAttributes.atlasIndex) + this._gl.enableVertexAttribArray(vertexShaderAttributes.atlasLayerIndex) this._gl.vertexAttribPointer( - vertexShaderAttributes.atlasIndex, + vertexShaderAttributes.atlasLayerIndex, 1, this._gl.FLOAT, false, glyphInstanceSizeInBytes, 8 * Float32Array.BYTES_PER_ELEMENT, ) - this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasIndex, 1) + this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasLayerIndex, 1) this._gl.enableVertexAttribArray(vertexShaderAttributes.atlasOrigin) this._gl.vertexAttribPointer( @@ -366,8 +366,8 @@ export class WebGlTextRenderer { this._glyphInstances[5 + startOffset] = color[1] this._glyphInstances[6 + startOffset] = color[2] this._glyphInstances[7 + startOffset] = color[3] - // atlasIndex - this._glyphInstances[8 + startOffset] = glyph.textureIndex + // atlasLayerIndex + this._glyphInstances[8 + startOffset] = glyph.textureLayerIndex // atlasOrigin this._glyphInstances[9 + startOffset] = glyph.textureU this._glyphInstances[10 + startOffset] = glyph.textureV From ffc2b0f27d4c4c11b33d1e74fbc2d0e25824a9bb Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sun, 13 May 2018 10:50:49 +0200 Subject: [PATCH 026/102] Add a proper fix for the null error --- browser/src/Renderer/WebGL/WebGLRenderer.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLRenderer.ts b/browser/src/Renderer/WebGL/WebGLRenderer.ts index 6415ee6b90..412e941a28 100644 --- a/browser/src/Renderer/WebGL/WebGLRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLRenderer.ts @@ -20,10 +20,19 @@ export class WebGLRenderer implements INeovimRenderer { public start(editorElement: HTMLElement): void { this._editorElement = editorElement this._colorNormalizer = new CachedColorNormalizer() + + const canvasElement = document.createElement("canvas") + this._editorElement.innerHTML = "" + this._editorElement.appendChild(canvasElement) + + this._gl = canvasElement.getContext("webgl2") as WebGL2RenderingContext } public redrawAll(screenInfo: IScreen): void { - this._initializeCanvasIfRequired() + if (!this._editorElement) { + return + } + this._updateCanvasDimensions() this._createNewRendererIfRequired(screenInfo) this._clear(screenInfo.backgroundColor) @@ -48,16 +57,6 @@ export class WebGLRenderer implements INeovimRenderer { // do nothing } - private _initializeCanvasIfRequired() { - if (!this._gl) { - const canvasElement = document.createElement("canvas") - this._editorElement.innerHTML = "" - this._editorElement.appendChild(canvasElement) - - this._gl = canvasElement.getContext("webgl2") as WebGL2RenderingContext - } - } - private _updateCanvasDimensions() { const devicePixelRatio = window.devicePixelRatio const canvas = this._gl.canvas From 285fec2a5aef7b84d0724f01a49a9dc894db9cdb Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sun, 13 May 2018 13:32:05 +0200 Subject: [PATCH 027/102] Add bold and italic rendering to the WebGLRenderer --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 49 ++++++++++++++----- .../src/Renderer/WebGL/WebGLTextRenderer.ts | 2 +- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index d671917b73..9dd909430c 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -23,7 +23,7 @@ export interface WebGLGlyph { export class WebGLAtlas { private _glyphContext: CanvasRenderingContext2D - private _glyphs = new Map>() + private _glyphs = new Map() private _nextX = 0 private _nextY = 0 private _textureChangedSinceLastUpload = false @@ -39,7 +39,6 @@ export class WebGLAtlas { glyphCanvas.width = this._textureSize glyphCanvas.height = this._textureSize this._glyphContext = glyphCanvas.getContext("2d", { alpha: false }) - this._glyphContext.font = `${this._options.fontSize} ${this._options.fontFamily}` this._glyphContext.fillStyle = "white" this._glyphContext.textBaseline = "top" this._glyphContext.scale(_options.devicePixelRatio, _options.devicePixelRatio) @@ -54,17 +53,26 @@ export class WebGLAtlas { this.uploadTexture() } - public getGlyph(text: string, variantIndex: number) { - let glyphVariants = this._glyphs.get(text) - if (!glyphVariants) { - glyphVariants = new Map() - this._glyphs.set(text, glyphVariants) + public getGlyph(text: string, isBold: boolean, isItalic: boolean, variantIndex: number) { + // The mapping goes from character to styles (bold etc.) to subpixel-offset variant, + // e.g. this._glyphs.get("a")[0][0] is the regular "a" with 0 offset, + // while this._glyphs.get("a")[3][1] is the bold italic "a" with 1/offsetGlyphVariantCount px offset + let glyphStyleVariants = this._glyphs.get(text) + if (!glyphStyleVariants) { + glyphStyleVariants = new Array(glyphStyles.length) + this._glyphs.set(text, glyphStyleVariants) + } + const glyphStyleIndex = getGlyphStyleIndex(isBold, isItalic) + let glyphOffsetVariants = glyphStyleVariants[glyphStyleIndex] + if (!glyphOffsetVariants) { + glyphOffsetVariants = new Array(this._options.offsetGlyphVariantCount) + glyphStyleVariants[glyphStyleIndex] = glyphOffsetVariants } - let glyph = glyphVariants.get(variantIndex) + let glyph = glyphOffsetVariants[variantIndex] if (!glyph) { - glyph = this._rasterizeGlyph(text, variantIndex) - glyphVariants.set(variantIndex, glyph) + glyph = this._rasterizeGlyph(text, isBold, isItalic, variantIndex) + glyphOffsetVariants[variantIndex] = glyph } return glyph @@ -87,7 +95,12 @@ export class WebGLAtlas { } } - private _rasterizeGlyph(text: string, variantIndex: number) { + private _rasterizeGlyph( + text: string, + isBold: boolean, + isItalic: boolean, + variantIndex: number, + ) { this._textureChangedSinceLastUpload = true const { @@ -114,6 +127,8 @@ export class WebGLAtlas { const x = this._nextX const y = this._nextY + const style = getGlyphStyleString(isBold, isItalic) + this._glyphContext.font = `${style} ${this._options.fontSize} ${this._options.fontFamily}` this._glyphContext.fillText(text, x + variantOffset, y + linePaddingInPixels / 2) this._nextX += width @@ -129,3 +144,15 @@ export class WebGLAtlas { } as WebGLGlyph } } + +const getGlyphStyleIndex = (isBold: boolean, isItalic: boolean) => + isBold ? (isItalic ? 3 : 1) : isItalic ? 2 : 0 + +const glyphStyles = [ + "", // regular, 0 + "bold", // 1 + "italic", // 2 + "bold italic", // 3 +] +const getGlyphStyleString = (isBold: boolean, isItalic: boolean) => + glyphStyles[getGlyphStyleIndex(isBold, isItalic)] diff --git a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts index a3a36124ca..893ebf0005 100644 --- a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts @@ -258,7 +258,7 @@ export class WebGlTextRenderer { if (!isWhiteSpace(char)) { const variantIndex = Math.round(x * this._subpixelDivisor) % this._subpixelDivisor - const glyph = this._atlas.getGlyph(char, variantIndex) + const glyph = this._atlas.getGlyph(char, cell.bold, cell.italic, variantIndex) const colorToUse = cell.foregroundColor || defaultForegroundColor || "white" const normalizedTextColor = this._colorNormalizer.normalizeColor(colorToUse) From 75cfb53858cdf9931849fe52e28be6258934dc1e Mon Sep 17 00:00:00 2001 From: Akin Date: Mon, 14 May 2018 17:46:51 +0100 Subject: [PATCH 028/102] Bugfix: Accumulating error squiggle styles in document head (#2197) * use attrs for error squiggle and marker components upgrade styled-components * return empty string not undefined as this is cast to string and added to the css --- browser/src/Plugins/PluginManager.ts | 11 +- browser/src/Services/Sneak/Sneak.tsx | 9 +- .../SyntaxHighlighting/TokenThemeProvider.tsx | 6 +- browser/src/UI/components/Error.tsx | 31 +- browser/src/UI/components/common.ts | 2 + package.json | 382 +++++------------- yarn.lock | 39 +- 7 files changed, 169 insertions(+), 311 deletions(-) diff --git a/browser/src/Plugins/PluginManager.ts b/browser/src/Plugins/PluginManager.ts index 4c0c19b32d..e751cde4a4 100644 --- a/browser/src/Plugins/PluginManager.ts +++ b/browser/src/Plugins/PluginManager.ts @@ -37,7 +37,7 @@ export class PluginManager implements Oni.IPluginManager { constructor(private _config: Configuration) {} public addDevelopmentPlugin(pluginPath: string): void { - this._developmentPluginsPath.push(pluginPath) + this._developmentPluginsPath.push(pluginPath) } public discoverPlugins(): void { @@ -61,7 +61,9 @@ export class PluginManager implements Oni.IPluginManager { this._createPlugin(p, "user"), ) - const developmentPlugins = this._developmentPluginsPath.map((dev) => this._createPlugin(dev, "development")) + const developmentPlugins = this._developmentPluginsPath.map(dev => + this._createPlugin(dev, "development"), + ) this._rootPluginPaths = [ ...corePluginRootPaths, @@ -84,7 +86,10 @@ export class PluginManager implements Oni.IPluginManager { } public getAllRuntimePaths(): string[] { - const pluginPaths = [...this._getAllPluginPaths(this._rootPluginPaths), ...this._developmentPluginsPath] + const pluginPaths = [ + ...this._getAllPluginPaths(this._rootPluginPaths), + ...this._developmentPluginsPath, + ] return pluginPaths.concat(this._rootPluginPaths) } diff --git a/browser/src/Services/Sneak/Sneak.tsx b/browser/src/Services/Sneak/Sneak.tsx index 21174c54fc..bb3e3baedb 100644 --- a/browser/src/Services/Sneak/Sneak.tsx +++ b/browser/src/Services/Sneak/Sneak.tsx @@ -12,7 +12,12 @@ import { Event, IDisposable, IEvent } from "oni-types" import { Overlay, OverlayManager } from "./../Overlay" -import { createStore as createSneakStore, IAugmentedSneakInfo, ISneakInfo, ISneakState } from "./SneakStore" +import { + createStore as createSneakStore, + IAugmentedSneakInfo, + ISneakInfo, + ISneakState, +} from "./SneakStore" import { ConnectedSneakView } from "./SneakView" export type SneakProvider = () => Promise @@ -43,7 +48,6 @@ export class Sneak { // Get the first sneak with a 'tag' matching the passed in tag public getSneakMatchingTag(tag: string): IAugmentedSneakInfo | null { - if (!this.isActive) { return null } @@ -55,7 +59,6 @@ export class Sneak { } return sneaks.find(s => s.tag && s.tag === tag) - } public show(): void { diff --git a/browser/src/Services/SyntaxHighlighting/TokenThemeProvider.tsx b/browser/src/Services/SyntaxHighlighting/TokenThemeProvider.tsx index 3700f0e513..1cee7bcb97 100644 --- a/browser/src/Services/SyntaxHighlighting/TokenThemeProvider.tsx +++ b/browser/src/Services/SyntaxHighlighting/TokenThemeProvider.tsx @@ -32,9 +32,9 @@ const constructClassName = (token: string) => (theme: INewTheme) => { const tokenStyle = cssToken(theme, token) const cssClass = ` .${tokenAsClass} { - color: ${tokenStyle("foregroundColor")}; - ${tokenStyle("bold") && notPunctuation && "font-weight: bold"}; - ${tokenStyle("italic") && "font-style: italic"}; + color: ${tokenStyle("foregroundColor") || ""}; + ${tokenStyle("bold") && notPunctuation ? "font-weight: bold" : ""}; + ${tokenStyle("italic") ? "font-style: italic" : ""}; } ` return cssClass diff --git a/browser/src/UI/components/Error.tsx b/browser/src/UI/components/Error.tsx index 464c362409..1ca2e19ee3 100644 --- a/browser/src/UI/components/Error.tsx +++ b/browser/src/UI/components/Error.tsx @@ -12,7 +12,7 @@ import * as Oni from "oni-api" import { getColorFromSeverity } from "./../../Services/Diagnostics" import { Icon } from "./../Icon" -import { bufferScrollBarSize, styled, withProps } from "./common" +import { bufferScrollBarSize, pixel, styled, withProps } from "./common" export interface IErrorsProps { errors: types.Diagnostic[] @@ -102,9 +102,16 @@ const ErrorMarker = (props: IErrorMarkerProps) => ( ) -const ErrorMarkerWrapper = withProps<{ topOffset: number }>(styled.div)` +interface ErrorMarkerProps { + topOffset: number +} + +const ErrorMarkerWrapper = withProps(styled.div).attrs({ + style: (props: ErrorMarkerProps) => ({ + top: pixel(props.topOffset), + }), +})` position: absolute; - top: ${props => props.topOffset}px; right: ${bufferScrollBarSize}; opacity: 0.5; background-color: rgb(80, 80, 80); @@ -132,13 +139,15 @@ interface IErrorSquiggleProps { width: number color: string } -const ErrorSquiggle = withProps(styled.div)` + +const ErrorSquiggle = withProps(styled.div).attrs({ + style: (props: IErrorSquiggleProps) => ({ + top: pixel(props.y), + left: pixel(props.x), + height: pixel(props.height), + width: pixel(props.width), + borderBottom: `1px dashed ${props.color}`, + }), +})` position: absolute; - ${props => ` - top: ${props.y}px; - left: ${props.x}px; - height: ${props.height}px; - width: ${props.width}px; - border-bottom: 1px dashed ${props.color}; - `} ` diff --git a/browser/src/UI/components/common.ts b/browser/src/UI/components/common.ts index 95b2548821..1821b8c4a0 100644 --- a/browser/src/UI/components/common.ts +++ b/browser/src/UI/components/common.ts @@ -58,6 +58,8 @@ export function withProps( return styledFunction } +export const pixel = (v: string | number): string => `${v}px` + const darken = (c: string, deg = 0.15) => Color(c) .darken(deg) diff --git a/package.json b/package.json index 4d55df213f..8337a0061f 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,7 @@ "homepage": "https://www.onivim.io", "version": "0.3.5", "description": "Code editor with a modern twist on modal editing - powered by neovim.", - "keywords": [ - "vim", - "neovim", - "text", - "editor", - "ide", - "vim" - ], + "keywords": ["vim", "neovim", "text", "editor", "ide", "vim"], "main": "./lib/main/src/main.js", "bin": { "oni": "./cli/oni", @@ -51,39 +44,23 @@ "mac": { "artifactName": "${productName}-${version}-osx.${ext}", "category": "public.app-category.developer-tools", - "target": [ - "dmg" - ], - "files": [ - "bin/osx/**/*" - ] + "target": ["dmg"], + "files": ["bin/osx/**/*"] }, "linux": { "artifactName": "${productName}-${version}-${arch}-linux.${ext}", "maintainer": "bryphe@outlook.com", - "target": [ - "tar.gz", - "deb", - "rpm" - ] + "target": ["tar.gz", "deb", "rpm"] }, "win": { - "target": [ - "zip", - "dir" - ], - "files": [ - "bin/x86/**/*" - ] + "target": ["zip", "dir"], + "files": ["bin/x86/**/*"] }, "fileAssociations": [ { "name": "ADA source", "role": "Editor", - "ext": [ - "adb", - "ads" - ] + "ext": ["adb", "ads"] }, { "name": "Compiled AppleScript", @@ -103,20 +80,12 @@ { "name": "ASP document", "role": "Editor", - "ext": [ - "asp", - "asa" - ] + "ext": ["asp", "asa"] }, { "name": "ASP.NET document", "role": "Editor", - "ext": [ - "aspx", - "ascx", - "asmx", - "ashx" - ] + "ext": ["aspx", "ascx", "asmx", "ashx"] }, { "name": "BibTeX bibliography", @@ -131,13 +100,7 @@ { "name": "C++ source", "role": "Editor", - "ext": [ - "cc", - "cp", - "cpp", - "cxx", - "c++" - ] + "ext": ["cc", "cp", "cpp", "cxx", "c++"] }, { "name": "C# source", @@ -162,10 +125,7 @@ { "name": "Clojure source", "role": "Editor", - "ext": [ - "clj", - "cljs" - ] + "ext": ["clj", "cljs"] }, { "name": "Comma separated values", @@ -180,20 +140,12 @@ { "name": "CGI script", "role": "Editor", - "ext": [ - "cgi", - "fcgi" - ] + "ext": ["cgi", "fcgi"] }, { "name": "Configuration file", "role": "Editor", - "ext": [ - "cfg", - "conf", - "config", - "htaccess" - ] + "ext": ["cfg", "conf", "config", "htaccess"] }, { "name": "Cascading style sheet", @@ -218,10 +170,7 @@ { "name": "Erlang source", "role": "Editor", - "ext": [ - "erl", - "hrl" - ] + "ext": ["erl", "hrl"] }, { "name": "F-Script source", @@ -231,32 +180,17 @@ { "name": "Fortran source", "role": "Editor", - "ext": [ - "f", - "for", - "fpp", - "f77", - "f90", - "f95" - ] + "ext": ["f", "for", "fpp", "f77", "f90", "f95"] }, { "name": "Header", "role": "Editor", - "ext": [ - "h", - "pch" - ] + "ext": ["h", "pch"] }, { "name": "C++ header", "role": "Editor", - "ext": [ - "hh", - "hpp", - "hxx", - "h++" - ] + "ext": ["hh", "hpp", "hxx", "h++"] }, { "name": "Go source", @@ -266,28 +200,17 @@ { "name": "GTD document", "role": "Editor", - "ext": [ - "gtd", - "gtdlog" - ] + "ext": ["gtd", "gtdlog"] }, { "name": "Haskell source", "role": "Editor", - "ext": [ - "hs", - "lhs" - ] + "ext": ["hs", "lhs"] }, { "name": "HTML document", "role": "Editor", - "ext": [ - "htm", - "html", - "phtml", - "shtml" - ] + "ext": ["htm", "html", "phtml", "shtml"] }, { "name": "Include file", @@ -327,10 +250,7 @@ { "name": "JavaScript source", "role": "Editor", - "ext": [ - "js", - "htc" - ] + "ext": ["js", "htc"] }, { "name": "Java Server Page", @@ -355,14 +275,7 @@ { "name": "Lisp source", "role": "Editor", - "ext": [ - "lisp", - "cl", - "l", - "lsp", - "mud", - "el" - ] + "ext": ["lisp", "cl", "l", "lsp", "mud", "el"] }, { "name": "Log file", @@ -382,12 +295,7 @@ { "name": "Markdown document", "role": "Editor", - "ext": [ - "markdown", - "mdown", - "markdn", - "md" - ] + "ext": ["markdown", "mdown", "markdn", "md"] }, { "name": "Makefile source", @@ -397,29 +305,17 @@ { "name": "Mediawiki document", "role": "Editor", - "ext": [ - "wiki", - "wikipedia", - "mediawiki" - ] + "ext": ["wiki", "wikipedia", "mediawiki"] }, { "name": "MIPS assembler source", "role": "Editor", - "ext": [ - "s", - "mips", - "spim", - "asm" - ] + "ext": ["s", "mips", "spim", "asm"] }, { "name": "Modula-3 source", "role": "Editor", - "ext": [ - "m3", - "cm3" - ] + "ext": ["m3", "cm3"] }, { "name": "MoinMoin document", @@ -439,28 +335,17 @@ { "name": "OCaml source", "role": "Editor", - "ext": [ - "ml", - "mli", - "mll", - "mly" - ] + "ext": ["ml", "mli", "mll", "mly"] }, { "name": "Mustache document", "role": "Editor", - "ext": [ - "mustache", - "hbs" - ] + "ext": ["mustache", "hbs"] }, { "name": "Pascal source", "role": "Editor", - "ext": [ - "pas", - "p" - ] + "ext": ["pas", "p"] }, { "name": "Patch file", @@ -470,11 +355,7 @@ { "name": "Perl source", "role": "Editor", - "ext": [ - "pl", - "pod", - "perl" - ] + "ext": ["pl", "pod", "perl"] }, { "name": "Perl module", @@ -484,80 +365,47 @@ { "name": "PHP source", "role": "Editor", - "ext": [ - "php", - "php3", - "php4", - "php5" - ] + "ext": ["php", "php3", "php4", "php5"] }, { "name": "PostScript source", "role": "Editor", - "ext": [ - "ps", - "eps" - ] + "ext": ["ps", "eps"] }, { "name": "Property list", "role": "Editor", - "ext": [ - "dict", - "plist", - "scriptSuite", - "scriptTerminology" - ] + "ext": ["dict", "plist", "scriptSuite", "scriptTerminology"] }, { "name": "Python source", "role": "Editor", - "ext": [ - "py", - "rpy", - "cpy", - "python" - ] + "ext": ["py", "rpy", "cpy", "python"] }, { "name": "R source", "role": "Editor", - "ext": [ - "r", - "s" - ] + "ext": ["r", "s"] }, { "name": "Ragel source", "role": "Editor", - "ext": [ - "rl", - "ragel" - ] + "ext": ["rl", "ragel"] }, { "name": "Remind document", "role": "Editor", - "ext": [ - "rem", - "remind" - ] + "ext": ["rem", "remind"] }, { "name": "reStructuredText document", "role": "Editor", - "ext": [ - "rst", - "rest" - ] + "ext": ["rst", "rest"] }, { "name": "HTML with embedded Ruby", "role": "Editor", - "ext": [ - "rhtml", - "erb" - ] + "ext": ["rhtml", "erb"] }, { "name": "SQL with embedded Ruby", @@ -567,28 +415,17 @@ { "name": "Ruby source", "role": "Editor", - "ext": [ - "rb", - "rbx", - "rjs", - "rxml" - ] + "ext": ["rb", "rbx", "rjs", "rxml"] }, { "name": "Sass source", "role": "Editor", - "ext": [ - "sass", - "scss" - ] + "ext": ["sass", "scss"] }, { "name": "Scheme source", "role": "Editor", - "ext": [ - "scm", - "sch" - ] + "ext": ["scm", "sch"] }, { "name": "Setext document", @@ -636,10 +473,7 @@ { "name": "SWIG source", "role": "Editor", - "ext": [ - "i", - "swg" - ] + "ext": ["i", "swg"] }, { "name": "Tcl source", @@ -649,20 +483,12 @@ { "name": "TeX document", "role": "Editor", - "ext": [ - "tex", - "sty", - "cls" - ] + "ext": ["tex", "sty", "cls"] }, { "name": "Plain text document", "role": "Editor", - "ext": [ - "text", - "txt", - "utf8" - ] + "ext": ["text", "txt", "utf8"] }, { "name": "Textile document", @@ -682,32 +508,17 @@ { "name": "XML document", "role": "Editor", - "ext": [ - "xml", - "xsd", - "xib", - "rss", - "tld", - "pt", - "cpt", - "dtml" - ] + "ext": ["xml", "xsd", "xib", "rss", "tld", "pt", "cpt", "dtml"] }, { "name": "XSL stylesheet", "role": "Editor", - "ext": [ - "xsl", - "xslt" - ] + "ext": ["xsl", "xslt"] }, { "name": "Electronic business card", "role": "Editor", - "ext": [ - "vcf", - "vcard" - ] + "ext": ["vcf", "vcard"] }, { "name": "Visual Basic source", @@ -717,10 +528,7 @@ { "name": "YAML document", "role": "Editor", - "ext": [ - "yaml", - "yml" - ] + "ext": ["yaml", "yml"] }, { "name": "Text document", @@ -782,65 +590,95 @@ "scripts": { "precommit": "pretty-quick --staged", "prepush": "yarn run build && yarn run lint", - "build": "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins", - "build-debug": "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins", + "build": + "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins", + "build-debug": + "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins", "build:browser": "webpack --config browser/webpack.production.config.js", "build:browser-debug": "webpack --config browser/webpack.debug.config.js", "build:main": "cd main && tsc -p tsconfig.json", - "build:plugins": "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview", + "build:plugins": + "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview", "build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && yarn run build", - "build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn run build", + "build:plugin:oni-plugin-markdown-preview": + "cd extensions/oni-plugin-markdown-preview && yarn run build", "build:test": "yarn run build:test:unit && yarn run build:test:integration", "build:test:integration": "cd test && tsc -p tsconfig.json", "build:test:unit": "yarn run build:test:unit:browser && yarn run build:test:unit:main", - "build:test:unit:browser": "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", + "build:test:unit:browser": + "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", "build:test:unit:main": "rimraf lib_test/main && cd main && tsc -p tsconfig.test.json", "build:webview_preload": "cd webview_preload && tsc -p tsconfig.json", "check-cached-binaries": "node build/script/CheckBinariesForBuild.js", "copy-icons": "node build/CopyIcons.js", - "debug:test:unit:browser": "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", - "demo:screenshot": "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", - "demo:video": "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", - "dist:win:x86": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", - "dist:win:x64": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", - "pack:win": "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", + "debug:test:unit:browser": + "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "demo:screenshot": + "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", + "demo:video": + "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", + "dist:win:x86": + "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", + "dist:win:x64": + "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", + "pack:win": + "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", "test": "yarn run test:unit && yarn run test:integration", - "test:integration": "yarn run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", + "test:integration": + "yarn run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", "test:react": "jest --config ./jest.config.js ./ui-tests", "test:react:watch": "jest --config ./jest.config.js ./ui-tests --watch", "test:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage", - "test:unit:browser": "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "test:unit:browser": + "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", "test:unit": "yarn run test:unit:browser && yarn run test:unit:main && yarn run test:react", - "test:unit:main": "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", + "test:unit:main": + "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", "upload:dist": "node build/script/UploadDistributionBuildsToAzure", "fix-lint": "yarn run fix-lint:browser && yarn run fix-lint:main && yarn run fix-lint:test", - "fix-lint:browser": "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "fix-lint:browser": + "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "fix-lint:main": "tslint --fix --project main/tsconfig.json --config tslint.json", "fix-lint:test": "tslint --fix --project test/tsconfig.json --config tslint.json", "lint": "yarn run lint:browser && yarn run lint:main && yarn run lint:test", - "lint:browser": "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "lint:browser": + "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "lint:main": "tslint --project main/tsconfig.json --config tslint.json", - "lint:test": "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", + "lint:test": + "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", "pack": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --publish never", - "ccov:instrument": "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", - "ccov:test:browser": "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", - "ccov:remap:browser:html": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", - "ccov:remap:browser:lcov": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", + "ccov:instrument": + "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", + "ccov:test:browser": + "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", + "ccov:remap:browser:html": + "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", + "ccov:remap:browser:lcov": + "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", "ccov:clean": "rimraf coverage", "ccov:upload": "codecov", "launch": "electron lib/main/src/main.js", - "start": "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"", - "start-hot": "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", + "start": + "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"", + "start-hot": + "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", "start-not-dev": "cross-env electron main.js", - "watch:browser": "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", - "watch:plugins": "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview", + "watch:browser": + "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", + "watch:plugins": + "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview", "watch:plugins:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && tsc --watch", - "watch:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && tsc --watch", - "install:plugins": "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier", - "install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn install --prod", - "install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && yarn install --prod", + "watch:plugins:oni-plugin-markdown-preview": + "cd extensions/oni-plugin-markdown-preview && tsc --watch", + "install:plugins": + "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier", + "install:plugins:oni-plugin-markdown-preview": + "cd extensions/oni-plugin-markdown-preview && yarn install --prod", + "install:plugins:oni-plugin-prettier": + "cd extensions/oni-plugin-prettier && yarn install --prod", "postinstall": "yarn run install:plugins && electron-rebuild && opencollective postinstall", - "profile:webpack": "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" + "profile:webpack": + "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" }, "repository": { "type": "git", @@ -871,7 +709,7 @@ "redux-batched-subscribe": "^0.1.6", "shell-env": "^0.3.0", "shelljs": "0.7.7", - "styled-components": "^2.3.0", + "styled-components": "^3.2.6", "typescript": "^2.8.1", "vscode-css-languageserver-bin": "^1.2.1", "vscode-html-languageserver-bin": "^1.1.0", diff --git a/yarn.lock b/yarn.lock index dafabd626c..76930e7132 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4037,7 +4037,7 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.8.16, fbjs@^0.8.5, fbjs@^0.8.9: +fbjs@^0.8.16, fbjs@^0.8.5: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" dependencies: @@ -4804,10 +4804,6 @@ hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" -hoist-non-react-statics@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" - hoist-non-react-statics@^2.1.0, hoist-non-react-statics@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" @@ -5290,10 +5286,6 @@ is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - is-generator-fn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-1.0.0.tgz#969d49e1bb3329f6bb7f09089be26578b2ddd46a" @@ -8311,6 +8303,10 @@ react-hot-loader@^4.0.1: react-lifecycles-compat "^2.0.0" shallowequal "^1.0.2" +react-is@^16.3.1: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" + react-lifecycles-compat@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-2.0.2.tgz#00a23160eec17a43b94dd74f95d44a1a2c3c5ec1" @@ -9642,23 +9638,28 @@ style-loader@0.18.2: loader-utils "^1.0.2" schema-utils "^0.3.0" -styled-components@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.3.0.tgz#d9cf4574e140fea6426e48632ed0ca4494537718" +styled-components@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.2.6.tgz#99e6e75a746bdedd295a17e03dd1493055a1cc3b" dependencies: buffer "^5.0.3" css-to-react-native "^2.0.3" - fbjs "^0.8.9" - hoist-non-react-statics "^1.2.0" - is-function "^1.0.1" + fbjs "^0.8.16" + hoist-non-react-statics "^2.5.0" is-plain-object "^2.0.1" prop-types "^15.5.4" - stylis "^3.4.0" + react-is "^16.3.1" + stylis "^3.5.0" + stylis-rule-sheet "^0.0.10" supports-color "^3.2.3" -stylis@^3.4.0: - version "3.4.5" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.4.5.tgz#d7b9595fc18e7b9c8775eca8270a9a1d3e59806e" +stylis-rule-sheet@^0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" + +stylis@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" subarg@^1.0.0: version "1.0.0" From 5e597e3e7dff2331d49430c95f5ef6d54aa2090f Mon Sep 17 00:00:00 2001 From: Akin Date: Mon, 14 May 2018 17:58:11 +0100 Subject: [PATCH 029/102] Feature/add file drag and drop handler (#2179) * add file drag and drop handler component * move filedrop handler into separate component use editor container component to collect drop events * add comment to explain use of filedraghandler * fix comments typo * refactor ref setting into file handler and add global hooks remove hooks from neovim editor * move global drag event handlers * fix comment to match functionality * tweak comment merge upstream --- .../Editor/NeovimEditor/FileDropHandler.tsx | 60 +++++++++++++ .../src/Editor/NeovimEditor/NeovimEditor.tsx | 32 +++---- .../src/Editor/NeovimEditor/NeovimSurface.tsx | 90 ++++++++++--------- browser/src/UI/Shell/Shell.tsx | 15 ++++ 4 files changed, 133 insertions(+), 64 deletions(-) create mode 100644 browser/src/Editor/NeovimEditor/FileDropHandler.tsx diff --git a/browser/src/Editor/NeovimEditor/FileDropHandler.tsx b/browser/src/Editor/NeovimEditor/FileDropHandler.tsx new file mode 100644 index 0000000000..7309f24a6b --- /dev/null +++ b/browser/src/Editor/NeovimEditor/FileDropHandler.tsx @@ -0,0 +1,60 @@ +import * as React from "react" + +type SetRef = (elem: HTMLElement) => void + +interface IFileDropHandler { + handleFiles: (files: FileList) => void + children: (args: { setRef: SetRef }) => React.ReactElement<{ setRef: SetRef }> +} + +type DragTypeName = "ondragover" | "ondragleave" | "ondragenter" + +/** + * Gets a target element via a callback ref and attaches a file drop event listener callback + * N.B. the element cannot be obscured as this will prevent event transmission + * @name FileDropHandler + * @function + * + * @extends {React} + */ +export default class FileDropHandler extends React.Component { + private _target: HTMLElement + + public componentDidMount() { + this.addDropHandler() + } + + public setRef = (element: HTMLElement) => { + this._target = element + } + + public addDropHandler() { + if (!this._target) { + return + } + + const dragTypes = ["ondragenter", "ondragover", "ondragleave"] + + dragTypes.map((event: DragTypeName) => { + if (this._target[event]) { + this._target[event] = ev => { + ev.preventDefault() + ev.stopPropagation() + } + } + }) + + this._target.ondrop = async ev => { + const { files } = ev.dataTransfer + + if (files.length) { + await this.props.handleFiles(files) + } + ev.preventDefault() + } + } + + public render() { + return this.props.children({ setRef: this.setRef }) + } +} diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index 2db70bd8d9..d5f08f95e3 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -740,26 +740,6 @@ export class NeovimEditor extends Editor implements IEditor { ipcRenderer.on("open-file", (_evt: any, path: string) => { this._neovimInstance.command(`:e! ${path}`) }) - - // TODO: Factor this out to a react component - // enable opening a file via drag-drop - document.body.ondragover = ev => { - ev.preventDefault() - ev.stopPropagation() - } - - document.body.ondrop = ev => { - ev.preventDefault() - // TODO: the following line currently breaks explorer drag and drop functionality - ev.stopPropagation() - - const { files } = ev.dataTransfer - - if (files.length) { - const normalisedPaths = Array.from(files).map(f => normalizePath(f.path)) - this.openFiles(normalisedPaths, { openMode: Oni.FileOpenMode.Edit }) - } - } } public async blockInput( @@ -910,10 +890,10 @@ export class NeovimEditor extends Editor implements IEditor { return this.activeBuffer } - public async openFiles( + public openFiles = async ( files: string[], openOptions: Oni.FileOpenOptions = Oni.DefaultFileOpenOptions, - ): Promise { + ): Promise => { if (!files) { return this.activeBuffer } @@ -946,6 +926,13 @@ export class NeovimEditor extends Editor implements IEditor { commandManager.executeCommand(command, null) } + public _onFilesDropped = async (files: FileList) => { + if (files.length) { + const normalisedPaths = Array.from(files).map(f => normalizePath(f.path)) + await this.openFiles(normalisedPaths, { openMode: Oni.FileOpenMode.Edit }) + } + } + public async init( filesToOpen: string[], startOptions?: Partial, @@ -1060,6 +1047,7 @@ export class NeovimEditor extends Editor implements IEditor { return ( void onBufferClose?: (bufferId: number) => void onBufferSelect?: (bufferId: number) => void + onFileDrop?: (files: FileList) => void onImeStart: () => void onImeEnd: () => void onBounceStart: () => void @@ -67,49 +69,53 @@ class NeovimSurface extends React.Component { public render(): JSX.Element { return ( -
-
- -
-
-
(this._editor = e)}> - + + {({ setRef }) => ( +
+
+ +
+
+
(this._editor = e)}> + +
+
+ + + + +
+ + +
+ +
+ + +
-
- - - - -
- - -
- -
- - -
-
+ )} + ) } } diff --git a/browser/src/UI/Shell/Shell.tsx b/browser/src/UI/Shell/Shell.tsx index 4e92b0ce2f..3b02bdac95 100644 --- a/browser/src/UI/Shell/Shell.tsx +++ b/browser/src/UI/Shell/Shell.tsx @@ -77,4 +77,19 @@ export const render = (state: State.IState): void => { // tslint:disable-next-line if (global["window"]) { document.body.addEventListener("click", () => focusManager.enforceFocus()) + + // This is necessary to prevent electron's default behaviour on drag and dropping + // which replaces the webContent aka the entire editor with the text, NOT Good + // also DO Not Stop Propagation as this breaks other drag drop functionality + document.addEventListener("dragover", ev => { + ev.preventDefault() + }) + + document.addEventListener("dragenter", ev => { + ev.preventDefault() + }) + + document.addEventListener("drop", ev => { + ev.preventDefault() + }) } From 95cdae7b1950f659cb74b65cb4f638d419065040 Mon Sep 17 00:00:00 2001 From: Akin Date: Mon, 14 May 2018 20:17:59 +0100 Subject: [PATCH 030/102] Bugfix/add sneakability back to tabs (#2186) * pass selected tab id to sneak callback * [WIP] add ci test * actually add new file to vcs * Add a ci test to prevent sneakability regression * add getMatchingSneak to ciTest fix typo in getMatchingSneak --- browser/src/Services/Sneak/Sneak.tsx | 2 +- browser/src/UI/components/Tabs.tsx | 2 +- test/CiTests.ts | 1 + test/ci/TabBarSneakTest.ts | 53 ++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 test/ci/TabBarSneakTest.ts diff --git a/browser/src/Services/Sneak/Sneak.tsx b/browser/src/Services/Sneak/Sneak.tsx index bb3e3baedb..df921e2952 100644 --- a/browser/src/Services/Sneak/Sneak.tsx +++ b/browser/src/Services/Sneak/Sneak.tsx @@ -54,7 +54,7 @@ export class Sneak { const sneaks = this._store.getState().sneaks - if (sneaks || sneaks.length === 0) { + if (!sneaks || sneaks.length === 0) { return null } diff --git a/browser/src/UI/components/Tabs.tsx b/browser/src/UI/components/Tabs.tsx index ce2d7400f8..e3c321dc66 100644 --- a/browser/src/UI/components/Tabs.tsx +++ b/browser/src/UI/components/Tabs.tsx @@ -176,7 +176,7 @@ export class Tab extends React.PureComponent { } return ( - + this.props.onClickName(this.props.id)} tag={this.props.name}> (this._tab = e)} className={cssClasses} diff --git a/test/CiTests.ts b/test/CiTests.ts index 7c528bd336..73456f85d6 100644 --- a/test/CiTests.ts +++ b/test/CiTests.ts @@ -22,6 +22,7 @@ const CiTests = [ "Configuration.TypeScriptEditor.NewConfigurationTest", "Configuration.TypeScriptEditor.CompletionTest", + "TabBarSneakTest", "initVimPromptNotificationTest", "Editor.BuffersCursorTest", "Editor.ExternalCommandLineTest", diff --git a/test/ci/TabBarSneakTest.ts b/test/ci/TabBarSneakTest.ts new file mode 100644 index 0000000000..b220231721 --- /dev/null +++ b/test/ci/TabBarSneakTest.ts @@ -0,0 +1,53 @@ +/** + * Tab Bar Sneak Test + * + * This test ensures that a user can trigger the sneak functionality and + * navigate to a different buffer + */ + +import * as assert from "assert" + +import * as Oni from "oni-api" + +import { + createNewFile, + getElementByClassName, + getElementsBySelector, + getSingleElementBySelector, + getTemporaryFilePath, +} from "./Common" + +export const test = async (oni: Oni.Plugin.Api) => { + await oni.automation.waitForEditors() + + // Next, open a split in the current tab, and check the tab still remains dirty + oni.automation.sendKeys(":") + oni.automation.sendKeys("e! buffer1") + oni.automation.sendKeys("") + await oni.automation.sleep(1500) + + oni.automation.sendKeys(":") + oni.automation.sendKeys("e! buffer2") + oni.automation.sendKeys("") + await oni.automation.sleep(1500) + + await oni.automation.sendKeys("") + await oni.automation.sleep(500) + + const anyOni = oni as any + const sneak = anyOni.sneak.getSneakMatchingTag("buffer1") + const keys: string = sneak.triggerKeys.toLowerCase() + await anyOni.automation.sendKeysV2(keys) + await oni.automation.sleep(2500) + + const path = oni.editors.activeEditor.activeBuffer.filePath + assert.ok(path.includes("buffer1")) +} + +export const settings = { + config: { + "tabs.mode": "buffers", + "oni.loadInitVim": false, + }, + allowLogFailures: true, +} From a622ce25b165107b2a0eae5f27f88c606a01d4ba Mon Sep 17 00:00:00 2001 From: keforbes Date: Mon, 14 May 2018 14:17:00 -0600 Subject: [PATCH 031/102] cleanup tutorials --- .../src/Services/Learning/Tutorial/Notes.tsx | 4 ++ .../BeginningsAndEndingsTutorial.tsx | 6 +-- .../Tutorials/ChangeOperatorTutorial.tsx | 24 ++++++++++- .../Tutorial/Tutorials/CopyPasteTutorial.tsx | 21 +++++++++- ...Tutorial.tsx => InsertAndUndoTutorial.tsx} | 37 ++++++++++------ .../Tutorials/SearchInBufferTutorial.tsx | 22 +++++++--- .../Tutorial/Tutorials/WordMotionTutorial.tsx | 42 +++++++------------ .../Learning/Tutorial/Tutorials/index.tsx | 4 +- 8 files changed, 109 insertions(+), 51 deletions(-) rename browser/src/Services/Learning/Tutorial/Tutorials/{MoveAndInsertTutorial.tsx => InsertAndUndoTutorial.tsx} (61%) diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index 7f447efda6..828cc87fca 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -112,6 +112,10 @@ export const OKey = (): JSX.Element => { ) } +export const UKey = (): JSX.Element => { + return Undo a single change} /> +} + export const GGKey = (): JSX.Element => { return ( ", "normal"), + new Stages.SetBufferStage([Line1Fixed, Line2]), + new Stages.MoveToGoalStage("Move to the goal marker", 1, Line2.indexOf("tedious")), + new Stages.WaitForStateStage("Change 'tedious' to 'fun'", [Line1Fixed, Line2Fix1]), + new Stages.WaitForModeStage("Exit Insert mode by hitting ", "normal"), + new Stages.MoveToGoalStage( + "Move to the goal marker", + 1, + Line2Fix1.indexOf("repetitive"), + ), + new Stages.WaitForStateStage("Change 'repetitive' to 'exciting'", [ + Line1Fixed, + Line2Fix2, + ]), + new Stages.WaitForModeStage("Exit Insert mode by hitting ", "normal"), ] } @@ -43,6 +60,11 @@ export class ChangeOperatorTutorial implements ITutorial { } public get notes(): JSX.Element[] { - return [, , ] + return [ + , + , + , + , + ] } } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx index 00419d11f6..9106cc5a1d 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/CopyPasteTutorial.tsx @@ -16,7 +16,12 @@ const Line2YankMarker = "Any deleted ".length const Line2PasteMarker = "Any deleted text or yanked".length const Line2PostPaste1 = "Any deleted text or yanked text can then be pasted with 'p'" const Line2PostPaste2 = "text Any deleted text or yanked text can then be pasted with 'p'" -const Line1PostTranspose = "iLke the 'd' operator, 'y' can be used to yank (copy) text" + +const TransposeLine = "Sipmle tpyos can aslo be fiexd with 'xp'" +const TransposeLine1 = "Simple tpyos can aslo be fiexd with 'xp'" +const TransposeLine2 = "Simple typos can aslo be fiexd with 'xp'" +const TransposeLine3 = "Simple typos can also be fiexd with 'xp'" +const TransposeLine4 = "Simple typos can also be fixed with 'xp'" export class CopyPasteTutorial implements ITutorial { private _stages: ITutorialStage[] @@ -64,10 +69,22 @@ export class CopyPasteTutorial implements ITutorial { Line1, Line2PostPaste2, ]), + new Stages.WaitForStateStage( + "Copied text can be pasted multiple times, past again with 'p'", + [Line2PostPaste2, Line2PostPaste2, Line1, Line1, Line2PostPaste2], + ), + new Stages.SetBufferStage([TransposeLine]), + new Stages.MoveToGoalStage("Move to the first typo", 0, 2), new Stages.WaitForStateStage( "Since deleted text is also copied, transposing characters is simple. Try 'xp'", - [Line2PostPaste2, Line2PostPaste2, Line1PostTranspose, Line2PostPaste2], + [TransposeLine1], ), + new Stages.MoveToGoalStage("Move to the next typo", 0, 8), + new Stages.WaitForStateStage("Again, fix the typo with 'xp'", [TransposeLine2]), + new Stages.MoveToGoalStage("Move to the next typo", 0, 18), + new Stages.WaitForStateStage("Again, fix the typo with 'xp'", [TransposeLine3]), + new Stages.MoveToGoalStage("Move to the next typo", 0, 27), + new Stages.WaitForStateStage("Again, fix the typo with 'xp'", [TransposeLine4]), ] } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/MoveAndInsertTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/InsertAndUndoTutorial.tsx similarity index 61% rename from browser/src/Services/Learning/Tutorial/Tutorials/MoveAndInsertTutorial.tsx rename to browser/src/Services/Learning/Tutorial/Tutorials/InsertAndUndoTutorial.tsx index f334bd99ea..aade7e9cea 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/MoveAndInsertTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/InsertAndUndoTutorial.tsx @@ -1,5 +1,5 @@ /** - * MoveAndInsertTutorial.tsx + * InsertAndUndoTutorial.tsx * * Tutorial that brings together moving and inserting */ @@ -10,10 +10,10 @@ import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" import * as Notes from "./../Notes" import * as Stages from "./../Stages" -const TutorialLine1Original = "There is text msng this ." +const TutorialLine1Original = "There is text msing this ." const TutorialLine1Correct = "There is some text missing from this line." -export class MoveAndInsertTutorial implements ITutorial { +export class InsertAndUndoTutorial implements ITutorial { private _stages: ITutorialStage[] constructor() { @@ -22,16 +22,16 @@ export class MoveAndInsertTutorial implements ITutorial { new Stages.MoveToGoalStage("Move to the letter 't'", 0, 9), new Stages.WaitForModeStage("Press 'i' to enter insert mode", "insert"), new Stages.CorrectLineStage( - "Add the missing word 'some'", + "Add the missing word 'some '", 0, TutorialLine1Correct, "green", - "There is some", + "There is some ", ), new Stages.WaitForModeStage("Press '' to exit insert mode", "normal"), new Stages.MoveToGoalStage("Move to the letter 's'", 0, 20), new Stages.CorrectLineStage( - "Correct the word: `msng` should be `missing`", + "Correct the word: `msing` should be `missing`", 0, TutorialLine1Correct, "green", @@ -42,7 +42,7 @@ export class MoveAndInsertTutorial implements ITutorial { 0, TutorialLine1Correct, "green", - "There is some text missing from", + "There is some text missing from ", ), new Stages.CorrectLineStage( "Add the missing word 'line'", @@ -51,16 +51,29 @@ export class MoveAndInsertTutorial implements ITutorial { "green", "There is some text missing from this line.", ), + new Stages.WaitForModeStage("Press '' to exit insert mode", "normal"), + new Stages.WaitForStateStage("Press 'u' to undo the last change", [ + "There is some text missing from this .", + TutorialLine1Correct, + ]), + new Stages.WaitForStateStage("Press 'u' to undo another change", [ + "There is some text missing this .", + TutorialLine1Correct, + ]), + new Stages.WaitForStateStage("Press 'u' to undo yet another change", [ + "There is some text msing this .", + TutorialLine1Correct, + ]), ] } public get metadata(): ITutorialMetadata { return { - id: "oni.tutorial.move_and_insert", - name: "Moving and Inserting", + id: "oni.tutorial.insert_and_undo", + name: "Insert and Undo", description: - "It's important to be able to switch between normal and insert mode, in order to edit text! Let's put together the cursor motion and insert mode from the previous tutorials.", - level: 130, + "It's important to be able to switch between normal and insert mode, in order to edit text! Let's put together the cursor motion and insert mode from the previous tutorials. If you make any mistakes, you can undo inserted text with 'u'.", + level: 170, } } @@ -69,6 +82,6 @@ export class MoveAndInsertTutorial implements ITutorial { } public get notes(): JSX.Element[] { - return [, , ] + return [, , , ] } } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/SearchInBufferTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/SearchInBufferTutorial.tsx index 0511ae155f..d62edfc656 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/SearchInBufferTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/SearchInBufferTutorial.tsx @@ -33,7 +33,11 @@ export class SearchInBufferTutorial implements ITutorial { this._stages = [ // Forward search new Stages.SetBufferStage([Line1, Line2]), - new Stages.MoveToGoalStage("Use '/' to search for the word 'move'", 1, 28), + new Stages.MoveToGoalStage( + "Use '/' to enter search mode, type the word 'move', then hit ", + 1, + 28, + ), new Stages.SetBufferStage([Line1, Line2, Line3]), new Stages.MoveToGoalStage("Use 'n' to go to the next instance of 'move'", 2, 21), new Stages.SetBufferStage([Line1, Line2, Line3, EmptyLine, EmptyLine, Line4]), @@ -53,11 +57,19 @@ export class SearchInBufferTutorial implements ITutorial { // Backward search new Stages.SetBufferStage([Line7]), new Stages.SetCursorPositionStage(0, 48), - new Stages.MoveToGoalStage("Use '?' to search for the word 'you'", 0, 21), + new Stages.MoveToGoalStage("Use '?' to search backwards for the word 'you'", 0, 21), new Stages.SetBufferStage([Line7, Line8, Line9]), - new Stages.MoveToGoalStage("Use 'N' to go to the previous instance of 'you'", 2, 14), + new Stages.MoveToGoalStage( + "Use 'N' to go to the previous (backwards) instance of 'you'", + 2, + 14, + ), new Stages.SetBufferStage([Line7, Line8, Line9, Line10]), - new Stages.MoveToGoalStage("Use 'n' to go to the next instance of 'move'", 0, 21), + new Stages.MoveToGoalStage( + "Use 'n' to go to the next (backwards) instance of 'move'", + 0, + 21, + ), ] } @@ -67,7 +79,7 @@ export class SearchInBufferTutorial implements ITutorial { name: "Motion: /, ?, n, N", description: "To navigate a buffer efficiently, Oni lets you search for strings with `/` and `?`. `n` and `N` let you navigate quickly between the matches!", - level: 180, + level: 160, } } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/WordMotionTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/WordMotionTutorial.tsx index b137cd77b4..b9dc0ec616 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/WordMotionTutorial.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/WordMotionTutorial.tsx @@ -27,46 +27,36 @@ export class WordMotionTutorial implements ITutorial { new Stages.SetBufferStage([Line1, Line2]), new Stages.MoveToGoalStage("Use the 'j' key to move down a line", 1, 10 /* todo */), new Stages.MoveToGoalStage( - "Use the '0' key to move to the beginning of the line", + "Use the 'e' key to move to the end of the current word", 1, - 0, + 12, ), new Stages.MoveToGoalStage( - "Use the 'e' key to move to the end of the first word", + "Use the 'e' key to move to the end of the next word", 1, - 2, - ), - new Stages.MoveToGoalStage( - "Use the 'e' key to move to the end of the second word", - 1, - 6, + 15, ), new Stages.MoveToGoalStage( - "Use the 'e' key to move to the end of the third word", + "Use the 'e' key to move to the end of the next word", 1, - 8, + 20, ), new Stages.SetBufferStage([Line1, Line2, Line3]), - new Stages.MoveToGoalStage("Use the 'j' key to move down a line", 2, 8 /* todo */), - new Stages.MoveToGoalStage( - "Use the '$' key to move to the end of the line", - 2, - Line3.length - 1, - ), + new Stages.MoveToGoalStage("Use the 'j' key to move down a line", 2, 20 /* todo */), new Stages.MoveToGoalStage( - "Use the 'b' key to move to the beginning of the last word", + "Use the 'b' key to move to the beginning of the current word", 2, - Line3.length - "word.".length, + 17, ), new Stages.MoveToGoalStage( - "Use the 'b' key to move to the beginning of the second-to-last word", + "Use the 'b' key to move to the beginning of the previous word", 2, - Line3.length - "PREVIOUS word.".length, + 14, ), new Stages.MoveToGoalStage( - "Use the 'b' key to move to the beginning of the third-to-last word", + "Use the 'b' key to move to the beginning of the previous word", 2, - Line3.length - "the PREVIOUS word.".length, + 10, ), ] } @@ -76,8 +66,8 @@ export class WordMotionTutorial implements ITutorial { id: "oni.tutorials.word_motion", name: "Motion: w, e, b", description: - "Often, `h` and `l` aren't the fastest way to move in a line. Word motions can be useful here - and even more useful when coupled with operators (we'll explore those later)! The `w` key moves to the first letter of the next word, the `b` key moves to the beginning letter of the previous word, and the `e` key moves to the end of the next word.", - level: 170, + "Often, `h` and `l` aren't the fastest way to move in a line. Word motions can be useful here - and even more useful when coupled with operators (we'll explore those later)! The `w` key moves to the first letter of the next word, the `e` key moves to the end of the next word, and the `b` key moves to the beginning letter of the previous word.", + level: 130, } } @@ -86,6 +76,6 @@ export class WordMotionTutorial implements ITutorial { } public get notes(): JSX.Element[] { - return [, , , ] + return [, , , ] } } diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx index 44136f7da1..0e743f2e2b 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx @@ -10,7 +10,7 @@ import { ChangeOperatorTutorial } from "./ChangeOperatorTutorial" import { CopyPasteTutorial } from "./CopyPasteTutorial" import { DeleteCharacterTutorial } from "./DeleteCharacterTutorial" import { DeleteOperatorTutorial } from "./DeleteOperatorTutorial" -import { MoveAndInsertTutorial } from "./MoveAndInsertTutorial" +import { InsertAndUndoTutorial } from "./InsertAndUndoTutorial" import { SearchInBufferTutorial } from "./SearchInBufferTutorial" import { SwitchModeTutorial } from "./SwitchModeTutorial" import { VerticalMovementTutorial } from "./VerticalMovementTutorial" @@ -26,7 +26,7 @@ export const AllTutorials: ITutorial[] = [ new BasicMovementTutorial(), new DeleteCharacterTutorial(), new DeleteOperatorTutorial(), - new MoveAndInsertTutorial(), + new InsertAndUndoTutorial(), new VerticalMovementTutorial(), new WordMotionTutorial(), new SearchInBufferTutorial(), From 892232f9b75ad1a2a76ec1b8763326a7d42ccc2b Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Mon, 14 May 2018 22:33:43 +0200 Subject: [PATCH 032/102] Add glyph padding and overlapping sections of the rendered glyph quads to fix the artifacts --- browser/src/Renderer/WebGL/WebGLAtlas.ts | 23 ++++++++++++------- browser/src/Renderer/WebGL/WebGLRenderer.ts | 1 + .../src/Renderer/WebGL/WebGLTextRenderer.ts | 9 +++++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLAtlas.ts b/browser/src/Renderer/WebGL/WebGLAtlas.ts index 5f5c9078aa..229c17c961 100644 --- a/browser/src/Renderer/WebGL/WebGLAtlas.ts +++ b/browser/src/Renderer/WebGL/WebGLAtlas.ts @@ -6,6 +6,7 @@ export interface IWebGLAtlasOptions { fontSize: string lineHeightInPixels: number linePaddingInPixels: number + glyphPaddingInPixels: number devicePixelRatio: number offsetGlyphVariantCount: number textureSizeInPixels: number @@ -21,7 +22,7 @@ export interface WebGLGlyph { textureU: number textureV: number variantOffset: number - subpixelWidth: number + subpixelWidth: number // TODO remove this as it is unused } export class WebGLTextureSpaceExceededError extends Error {} @@ -140,13 +141,17 @@ export class WebGLAtlas { devicePixelRatio, lineHeightInPixels, linePaddingInPixels, + glyphPaddingInPixels, offsetGlyphVariantCount, } = this._options + const style = getGlyphStyleString(isBold, isItalic) + this._glyphContext.font = `${style} ${this._options.fontSize} ${this._options.fontFamily}` const variantOffset = variantIndex / offsetGlyphVariantCount - const height = lineHeightInPixels - const { width: subpixelWidth } = this._glyphContext.measureText(text) - const width = Math.ceil(variantOffset) + Math.ceil(subpixelWidth) + const height = lineHeightInPixels + 2 * glyphPaddingInPixels + const { width: measuredGlyphWidth } = this._glyphContext.measureText(text) + const width = + Math.ceil(variantOffset) + Math.ceil(measuredGlyphWidth) + 2 * glyphPaddingInPixels if ((this._nextX + width) * devicePixelRatio > this._options.textureSizeInPixels) { this._nextX = 0 @@ -159,9 +164,11 @@ export class WebGLAtlas { const x = this._nextX const y = this._nextY - const style = getGlyphStyleString(isBold, isItalic) - this._glyphContext.font = `${style} ${this._options.fontSize} ${this._options.fontFamily}` - this._glyphContext.fillText(text, x + variantOffset, y + linePaddingInPixels / 2) + this._glyphContext.fillText( + text, + x + glyphPaddingInPixels + variantOffset, + y + glyphPaddingInPixels + linePaddingInPixels / 2, + ) this._nextX += width return { @@ -172,7 +179,7 @@ export class WebGLAtlas { textureV: y * devicePixelRatio / this._options.textureSizeInPixels, textureWidth: width * devicePixelRatio / this._options.textureSizeInPixels, textureHeight: height * devicePixelRatio / this._options.textureSizeInPixels, - subpixelWidth: subpixelWidth * devicePixelRatio, + subpixelWidth: measuredGlyphWidth * devicePixelRatio, variantOffset, } as WebGLGlyph } diff --git a/browser/src/Renderer/WebGL/WebGLRenderer.ts b/browser/src/Renderer/WebGL/WebGLRenderer.ts index 412e941a28..6a176b8ae3 100644 --- a/browser/src/Renderer/WebGL/WebGLRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLRenderer.ts @@ -82,6 +82,7 @@ export class WebGLRenderer implements INeovimRenderer { fontSize, lineHeightInPixels: fontHeightInPixels, linePaddingInPixels, + glyphPaddingInPixels: Math.ceil(fontHeightInPixels / 4), devicePixelRatio, offsetGlyphVariantCount, textureSizeInPixels: this._textureSizeInPixels, diff --git a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts index f112cdab83..31b8808e47 100644 --- a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts @@ -93,6 +93,7 @@ const isWhiteSpace = (text: string) => text === null || text === "" || text === export class WebGlTextRenderer { private _atlas: WebGLAtlas + private _glyphOverlapInPixels: number private _subpixelDivisor: number private _devicePixelRatio: number @@ -113,8 +114,9 @@ export class WebGlTextRenderer { private _colorNormalizer: IColorNormalizer, atlasOptions: IWebGLAtlasOptions, ) { - this._devicePixelRatio = atlasOptions.devicePixelRatio + this._glyphOverlapInPixels = atlasOptions.glyphPaddingInPixels this._subpixelDivisor = atlasOptions.offsetGlyphVariantCount + this._devicePixelRatio = atlasOptions.devicePixelRatio this._atlas = new WebGLAtlas(this._gl, atlasOptions) this._firstPassProgram = createProgram( @@ -274,6 +276,7 @@ export class WebGlTextRenderer { ) { const pixelRatioAdaptedFontWidth = fontWidthInPixels * this._devicePixelRatio const pixelRatioAdaptedFontHeight = fontHeightInPixels * this._devicePixelRatio + const pixelRatioAdaptedGlyphOverlap = this._glyphOverlapInPixels * this._devicePixelRatio let glyphCount = 0 let y = 0 @@ -294,8 +297,8 @@ export class WebGlTextRenderer { this.updateGlyphInstance( glyphCount, - Math.round(x - glyph.variantOffset), - y, + Math.round(x - glyph.variantOffset) - pixelRatioAdaptedGlyphOverlap, + y - pixelRatioAdaptedGlyphOverlap, glyph, normalizedTextColor, ) From db90b584a2139de14e0d6c12c69aa06c46a5a307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Kosi=C5=84ski?= Date: Tue, 15 May 2018 18:26:59 +0200 Subject: [PATCH 033/102] Add issue template (#2203) --- ISSUE_TEMPLATE.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..79e0b61b6b --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +- oni version: +- neovim version (linux only): +- operating system: + +### Describe your issue + +### Expected behaviour + +### Actual behaviour + +### Steps to reproduce From d2878545bb4ed0bb377153e1a0434691ab81c461 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 15 May 2018 10:16:29 -0700 Subject: [PATCH 034/102] #4332 - Fix bug with 'onScrolled' event not firing in all cases (#2143) * Use the 'scroll' event from redraw as a hitn that we should dispatch the scroll event * Add CiTest to validate scroll event * Fix up test * Fix up CiTest for scroll event --- browser/src/neovim/NeovimInstance.ts | 40 +++++++----- browser/src/neovim/NeovimWindowManager.ts | 1 - test/CiTests.ts | 2 +- test/ci/Editor.ScrollEventTest.ts | 79 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 test/ci/Editor.ScrollEventTest.ts diff --git a/browser/src/neovim/NeovimInstance.ts b/browser/src/neovim/NeovimInstance.ts index 53ea8f940f..f2ecc0eb74 100644 --- a/browser/src/neovim/NeovimInstance.ts +++ b/browser/src/neovim/NeovimInstance.ts @@ -384,7 +384,12 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { this._bufferUpdateManager.notifyModeChanged(newMode) }) - this._disposables = [s1] + const dispatchScroll = () => this._dispatchScrollEvent() + + const s2 = this._autoCommands.onCursorMoved.subscribe(dispatchScroll) + const s3 = this._autoCommands.onCursorMovedI.subscribe(dispatchScroll) + + this._disposables = [s1, s2, s3] } public dispose(): void { @@ -656,22 +661,6 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { return versionInfo[1].version as any } - public dispatchScrollEvent(): void { - if (this._pendingScrollTimeout || this._isDisposed) { - return - } - - this._pendingScrollTimeout = window.setTimeout(async () => { - if (this._isDisposed) { - return - } - - const evt = await this.getContext() - this._onScroll.dispatch(evt) - this._pendingScrollTimeout = null - }) - } - public async quit(): Promise { // This command won't resolve the promise (since it's quitting), // so we're not awaiting.. @@ -701,6 +690,22 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { } } + private _dispatchScrollEvent(): void { + if (this._pendingScrollTimeout || this._isDisposed) { + return + } + + this._pendingScrollTimeout = window.setTimeout(async () => { + if (this._isDisposed) { + return + } + + const evt = await this.getContext() + this._onScroll.dispatch(evt) + this._pendingScrollTimeout = null + }) + } + private _resizeInternal(rows: number, columns: number): void { if (this._configuration.hasValue("debug.fixedSize")) { const fixedSize = this._configuration.getValue("debug.fixedSize") @@ -760,6 +765,7 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { break case "scroll": this.emit("action", Actions.scroll(a[0][0])) + this._dispatchScrollEvent() break case "highlight_set": const highlightInfo = a[a.length - 1][0] diff --git a/browser/src/neovim/NeovimWindowManager.ts b/browser/src/neovim/NeovimWindowManager.ts index 7d20e5aef3..2f063a3756 100644 --- a/browser/src/neovim/NeovimWindowManager.ts +++ b/browser/src/neovim/NeovimWindowManager.ts @@ -105,7 +105,6 @@ export class NeovimWindowManager extends Utility.Disposable { }) .subscribe((tabState: NeovimTabPageState) => { this._onWindowStateChangedEvent.dispatch(tabState) - this._neovimInstance.dispatchScrollEvent() }) } diff --git a/test/CiTests.ts b/test/CiTests.ts index 73456f85d6..293604259a 100644 --- a/test/CiTests.ts +++ b/test/CiTests.ts @@ -8,7 +8,6 @@ import * as mkdirp from "mkdirp" import { IFailedTest, Oni, runInProcTest } from "./common" const LongTimeout = 5000 - const CiTests = [ // Core functionality tests "Api.Buffer.AddLayer", @@ -28,6 +27,7 @@ const CiTests = [ "Editor.ExternalCommandLineTest", "Editor.BufferModifiedState", "Editor.OpenFile.PathWithSpacesTest", + "Editor.ScrollEventTest", "Editor.TabModifiedState", "Editor.CloseTabWithTabModesTabsTest", "MarkdownPreviewTest", diff --git a/test/ci/Editor.ScrollEventTest.ts b/test/ci/Editor.ScrollEventTest.ts new file mode 100644 index 0000000000..30883dea1f --- /dev/null +++ b/test/ci/Editor.ScrollEventTest.ts @@ -0,0 +1,79 @@ +/** + * Test script to validate the modified status for tabs. + */ + +import * as assert from "assert" +import * as Oni from "oni-api" + +import { createNewFile, getElementByClassName } from "./Common" + +import * as os from "os" +const createLines = (num: number): string => { + const ret = [] + + for (let i = 0; i < num; i++) { + ret.push(i) + } + + return ret.join(os.EOL) +} + +const assertValue = (actual: number, expected: number, msg: string, oni: Oni.Plugin.Api) => { + const passed = actual === expected + + const notification = oni.notifications.createItem() + const title = passed ? "Assertion Passed" : "Assertion Failed" + notification.setContents(title, `${msg}\nActual: ${actual}\nExpected:${expected}`) + ;(notification as any).setLevel(passed ? "success" : "error") + notification.show() + + assert.strictEqual(actual, expected, msg) +} + +export const test = async (oni: Oni.Plugin.Api) => { + await oni.automation.waitForEditors() + + await createNewFile("js", oni, createLines(500)) + + let scrollEventHitCount = 0 + + oni.editors.activeEditor.onBufferScrolled.subscribe(() => { + scrollEventHitCount++ + }) + + await oni.automation.sendKeys("G") + + await oni.automation.waitFor(() => scrollEventHitCount === 1) + assertValue(scrollEventHitCount, 1, "A single scroll event should've been triggered by G", oni) + + await oni.automation.sendKeys("gg") + await oni.automation.waitFor(() => scrollEventHitCount === 2) + assertValue(scrollEventHitCount, 2, "Another scroll event should've been triggered by gg", oni) + + await oni.automation.sendKeys(":50") + await oni.automation.waitFor(() => scrollEventHitCount === 3) + assertValue( + scrollEventHitCount, + 3, + "Another scroll event should've been triggered by navigating to a line", + oni, + ) + + await oni.automation.sendKeys("") + await oni.automation.waitFor(() => scrollEventHitCount === 4) + assertValue( + scrollEventHitCount, + 4, + "Another scroll event should've been triggered by scrolling up one line", + oni, + ) + + await oni.automation.sendKeys("") + await oni.automation.waitFor(() => scrollEventHitCount === 5) + assertValue( + scrollEventHitCount, + 5, + "Another scroll event should've been triggered by scrolling down one line", + oni, + ) +} From 6dae5d1372bfd9a1e1cc3388c78de6108a8478c8 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 15 May 2018 10:21:58 -0700 Subject: [PATCH 035/102] Update intro video with 0.3.2 features (#2019) * Update HeroDemo to use new split mode * Fix issue with oni split mode not being set completely * Display achievement at beginning of video * Get achievement working * Continue improving test * Add code to create the demo app * Start implementing browser / terminal demo * Add 'sneak' to oni api * Add tags for sneak mode so its easier to automate * Show end-to-end development experience * Get test working end-to-end * Only put one alert in * Continue tweaking demo * Demo updates * Update demo * Update demo * Demo tweaks * Fix lint issues * Revert changes to sneak * Revert change to Sneakable * Revert change to Oni.ts --- .../AchievementNotificationRenderer.tsx | 7 +- .../Achievements/AchievementsManager.ts | 7 +- test/Demo.ts | 2 +- test/demo/HeroDemo.ts | 480 +++++++++++++----- 4 files changed, 375 insertions(+), 121 deletions(-) diff --git a/browser/src/Services/Learning/Achievements/AchievementNotificationRenderer.tsx b/browser/src/Services/Learning/Achievements/AchievementNotificationRenderer.tsx index 872a205c2b..4a6053f828 100644 --- a/browser/src/Services/Learning/Achievements/AchievementNotificationRenderer.tsx +++ b/browser/src/Services/Learning/Achievements/AchievementNotificationRenderer.tsx @@ -37,8 +37,11 @@ export class AchievementNotificationRenderer { const AchievementsWrapper = styled.div` & .achievements { - width: 100%; - height: 100%; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; display: flex; flex-direction: column; diff --git a/browser/src/Services/Learning/Achievements/AchievementsManager.ts b/browser/src/Services/Learning/Achievements/AchievementsManager.ts index 9151269416..83174d5958 100644 --- a/browser/src/Services/Learning/Achievements/AchievementsManager.ts +++ b/browser/src/Services/Learning/Achievements/AchievementsManager.ts @@ -107,10 +107,13 @@ export class AchievementsManager { } public clearAchievements(): void { - this._persistentStore.set({ + const clearedState: IPersistedAchievementState = { goalCounts: {}, achievedIds: [], - }) + } + + this._goalState = clearedState + this._persistentStore.set(clearedState) } public registerAchievement(definition: AchievementDefinition): void { diff --git a/test/Demo.ts b/test/Demo.ts index 44d1535b67..2429a98190 100644 --- a/test/Demo.ts +++ b/test/Demo.ts @@ -10,5 +10,5 @@ const TestToRun = process.env["DEMO_TEST"] // tslint:disable-line console.log("Running test: " + TestToRun) describe("demo tests", () => { - runInProcTest(path.join(__dirname, "demo"), TestToRun) + runInProcTest(path.join(__dirname, "demo"), TestToRun, 50000) }) diff --git a/test/demo/HeroDemo.ts b/test/demo/HeroDemo.ts index 6c7b45d4d2..67198a1b14 100644 --- a/test/demo/HeroDemo.ts +++ b/test/demo/HeroDemo.ts @@ -3,23 +3,82 @@ */ import * as assert from "assert" +import { execSync } from "child_process" +import * as fs from "fs" import * as os from "os" import * as path from "path" +import * as shell from "shelljs" + +import * as rimraf from "rimraf" import { getCompletionElement, getTemporaryFolder } from "./../ci/Common" -import { getDistPath } from "./DemoCommon" +import { getDistPath, getRootPath } from "./DemoCommon" import { remote } from "electron" +const BASEDELAY = 18 +const RANDOMDELAY = 8 + const EmptyConfigPath = path.join(getTemporaryFolder(), "config.js") -const BASEDELAY = 25 -const RANDOMDELAY = 15 +const getProjectRootPath = () => { + const root = getRoot(__dirname) + return os.platform() === "win32" ? root : os.homedir() +} + +const ReactProjectName = "oni-react-app" + +const getRoot = (dir: string): string => { + const parent = path.dirname(dir) + if (parent === dir) { + return parent + } else { + return getRoot(parent) + } +} + +const createReactAppProject = oni => { + const oniReactApp = path.join(getProjectRootPath(), ReactProjectName) + + // rimraf.sync(oniReactApp) + // const output = execSync('create-react-app "' + oniReactApp + '"') + + const oniLogoPath = path.join(getRootPath(), "images", "256x256.png") + const oniLogoDestinationPath = path.join(oniReactApp, "src", "oni.png") + + const oniLogoComponentPath = path.join(oniReactApp, "src", "OniLogo.js") + + fs.writeFileSync( + oniLogoComponentPath, + ` +import React, { Component } from 'react'; +import logo from './oni.png'; + +export class OniLogo extends Component { + render() { + return logo; + } +} + `, + "utf8", + ) + + // Delete the 'App.test.js' so it doesn't mess up fuzzy find results + rimraf.sync(path.join(oniReactApp, "src", "App.test.js")) + + shell.cp(oniLogoPath, oniLogoDestinationPath) + shell.cp(path.join(oniReactApp, "src", "Old.js"), path.join(oniReactApp, "src", "App.js")) + return oniReactApp +} export const test = async (oni: any) => { + const reactAppPath = createReactAppProject(oni) + await oni.automation.waitForEditors() + oni.workspace.changeDirectory(reactAppPath) + const isMac = process.platform === "darwin" const shortDelay = async () => oni.automation.sleep(BASEDELAY * 25) @@ -78,10 +137,221 @@ export const test = async (oni: any) => { await shortDelay() } + const splitHorizontal = async (fileName: string) => { + await shortDelay() + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + await simulateTyping(":tabnew VIM.md") + } + const waitForCompletion = async () => { return oni.automation.waitFor(() => !!getCompletionElement()) } + const showWelcomeAchievement = async () => { + oni.achievements.clearAchievements() + + // Create our own 'mock' achievement, because + // the welcome one won't be tracked if it has been completed + oni.achievements.registerAchievement({ + uniqueId: "oni.achievement.automation", + name: "Welcome to Oni!", + description: "Launch Oni for the first time", + goals: [ + { + name: null, + goalId: "oni.automation.goal", + count: 1, + }, + ], + }) + oni.achievements.notifyGoal("oni.automation.goal") + + await longDelay() + await longDelay() + } + + const intro = async () => { + await simulateTyping(":tabnew Hello.md") + await pressEnter() + + await simulateTyping( + "iOni is a new kind of editor: combining the best of Vim, Atom, and VSCode.", + ) + await pressEnter() + await simulateTyping( + "Built with web tech, featuring a high performance canvas renderer, with (neo)vim handling the heavy lifting.", + ) + await pressEnter() + await simulateTyping("Available for Windows, OSX, and Linux.") + + await pressEscape() + } + + const navigateToSneakWithTag = async (tag: string) => { + oni.automation.sendKeysV2("") + await shortDelay() + + const targetSneak = oni.sneak.getSneakMatchingTag(tag) + const triggerKeys = targetSneak.triggerKeys as string + await simulateTyping(triggerKeys) + await shortDelay() + } + + const showKeyboardNavigation = async () => { + await splitHorizontal("VIM.md") + await pressEnter() + + await simulateTyping("i") + await simulateTyping("Use your Vim muscle memory to be productive without a mouse...") + + await pressEscape() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + oni.automation.sendKeysV2("G") + await longDelay() + oni.automation.sendKeysV2("gg") + await longDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + await simulateTyping("o") + await simulateTyping("..but enjoy the conveniences of a modern UI editor.") + await pressEscape() + + await shortDelay() + + await navigateToSneakWithTag("oni.sidebar.search") + + await navigateToSneakWithTag("oni.sidebar.learning") + + await navigateToSneakWithTag("oni.sidebar.explorer") + + oni.automation.sendKeysV2("") + + await simulateTyping(":qa!") + oni.automation.sendKeysV2("") + + await shortDelay() + + oni.automation.sendKeysV2("") + } + + const showDevelopment = async () => { + await pressEscape() + + await openCommandPalette() + await simulateTyping("brovsp") + await pressEnter() + + await pressEscape() + await openCommandPalette() + await simulateTyping("termhzsp") + await pressEnter() + + await longDelay() + + await simulateTyping("A") + await simulateTyping("npm run start") + await pressEnter() + + await pressEscape() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + await openQuickOpen() + await simulateTyping("Appjs") + await pressEnter() + + await navigateToSneakWithTag("browser.address") + + await shortDelay() + + await simulateTyping("http://localhost:3000") + await pressEnter() + + // The popped up browser can steal focus, causing our bindings to fail.. + require("electron") + .remote.getCurrentWindow() + .focus() + + await simulateTyping("10j") + await shortDelay() + await simulateTyping("cit") + await shortDelay() + await simulateTyping("Welcome to Oni") + await pressEscape() + await simulateTyping(":w") + await pressEnter() + await shortDelay() + + await simulateTyping("7k") + await simulateTyping("O") + await simulateTyping("impsnip") + + await waitForCompletion() + + await pressEnter() + + await shortDelay() + await simulateTyping("./Oni") + await waitForCompletion() + await pressEnter() + + await pressTab() + await simulateTyping("Oni") + await waitForCompletion() + await pressEnter() + await pressTab() + + await simulateTyping("7j") + await simulateTyping("b") + await simulateTyping("C") + await simulateTyping("OniLogo />") + + await pressEscape() + await simulateTyping(":w") + await pressEnter() + await longDelay() + await longDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + oni.automation.sendKeysV2("") + oni.automation.sendKeysV2("") + await shortDelay() + + await simulateTyping(":q") + await pressEnter() + await shortDelay() + + await simulateTyping(":q") + await pressEnter() + await shortDelay() + } + const showConfig = async () => { await pressEscape() await openCommandPalette() @@ -105,24 +375,24 @@ export const test = async (oni: any) => { await longDelay() await simulateTyping("ciw") await longDelay() - await simulateTyping("15px") + await simulateTyping("16px") await pressEscape() await simulateTyping(":w") await pressEscape() // HACK - Configuration doesn't use the same file, so we need to set this directly here - oni.configuration.setValues({ "editor.fontSize": "15px" }) + oni.configuration.setValues({ "editor.fontSize": "16px" }) await longDelay() await simulateTyping("b") await longDelay() await simulateTyping("ciw") await longDelay() - await simulateTyping("12px") + await simulateTyping("14px") await pressEscape() await simulateTyping(":w") await pressEnter() - oni.configuration.setValues({ "editor.fontSize": "12px" }) + oni.configuration.setValues({ "editor.fontSize": "14px" }) await longDelay() await pressEscape() @@ -172,6 +442,12 @@ export const test = async (oni: any) => { // item.show() await longDelay() + + await simulateTyping(":q") + await pressEnter() + + await simulateTyping(":q") + await pressEnter() } const showComingSoon = async () => { @@ -184,11 +460,15 @@ export const test = async (oni: any) => { await simulateTyping("This is just the beginning! Lots more to come:") await pressEnter() - await simulateTyping("Live Preview") + await simulateTyping("* Live Preview") + await pressEnter() + await simulateTyping("* Plugin Management") await pressEnter() - await simulateTyping("Integrated Browser") + await simulateTyping("* More tutorials ") await pressEnter() - await simulateTyping("Interactive Tutorial") + await simulateTyping("* Debuggers") + await pressEnter() + await simulateTyping("* Version Control Integration") await longDelay() await pressEnter() await pressEnter() @@ -250,11 +530,44 @@ export const test = async (oni: any) => { await pressEscape() } + const showTutorials = async () => { + await oni.editors.activeEditor.neovim.command(":tabnew TUTORIAL") + + await simulateTyping("i") + await simulateTyping( + "If you're new to modal editing, Oni comes with interactive tutorials to get you up to speed!", + ) + await pressEscape() + + await navigateToSneakWithTag("oni.sidebar.learning") + + const firstTutorialId = oni.tutorials.getNextTutorialId() + await oni.tutorials.startTutorial(firstTutorialId) + + await shortDelay() + await pressEscape() + + await simulateTyping("i") + await shortDelay() + await simulateTyping("hello") + await shortDelay() + await pressEscape() + await shortDelay() + await simulateTyping("o") + await shortDelay() + await simulateTyping("world") + await shortDelay() + await pressEscape() + + await longDelay() + await oni.editors.activeEditor.neovim.command(":q!") + } + // Prime the typescript language service prior to recording await simulateTyping(":tabnew") await pressEnter() await openQuickOpen() - await simulateTyping("NeovimInstance.ts") + await simulateTyping("App.js") await pressEnter() await simulateTyping("owindow.") @@ -264,127 +577,62 @@ export const test = async (oni: any) => { await simulateTyping(":q!") await pressEnter() - // Set window size - remote.getCurrentWindow().setSize(1280, 720) + oni.configuration.setValues({ + "ui.fontSize": "14px", + "editor.fontSize": "14px", + }) - // Disable notifications, since there is sometimes noise... (HACK) - oni.notifications.disable() + // Set window size + remote.getCurrentWindow().setSize(1920, 1080) oni.recorder.startRecording() - oni.commands.executeCommand("keyDisplayer.show") - oni.configuration.setValues({ "keyDisplayer.showInInsertMode": false }) - - await simulateTyping(":tabnew Hello.md") - await pressEnter() - - await simulateTyping("iHello and welcome to Oni!") - await pressEnter() - - await simulateTyping( - "Oni is a new kind of editor: combining the best of Vim, Atom, and VSCode.", - ) - await pressEnter() - await simulateTyping( - "Built with web tech, featuring a high performance canvas renderer, with (neo)vim handling the heavy lifting.", - ) - await pressEnter() - await simulateTyping("Available for Windows, OSX, and Linux.") - await pressEnter() - - await pressEscape() - await simulateTyping(":sp VIM.md") - await pressEnter() - - await simulateTyping("i") - await simulateTyping("Use your Vim muscle memory to be productive without a mouse...") - - await pressEscape() - - oni.automation.sendKeysV2("") - oni.automation.sendKeysV2("") - await shortDelay() - - oni.automation.sendKeysV2("G") - await longDelay() - oni.automation.sendKeysV2("gg") - await longDelay() - - oni.automation.sendKeysV2("") - oni.automation.sendKeysV2("") - await shortDelay() + await showWelcomeAchievement() - oni.automation.sendKeysV2("") - oni.automation.sendKeysV2("") - await shortDelay() + oni.tutorials.clearProgress() - oni.automation.sendKeysV2("") - oni.automation.sendKeysV2("") - await shortDelay() - - await simulateTyping("o") - await simulateTyping("..but enjoy the conveniences of a modern UI editor.") - await pressEscape() - - await shortDelay() - oni.automation.sendKeysV2("") - await shortDelay() - await simulateTyping("a") - await shortDelay() - await simulateTyping("c") + oni.commands.executeCommand("keyDisplayer.show") + oni.configuration.setValues({ + "keyDisplayer.showInInsertMode": false, + "editor.split.mode": "oni", + "browser.defaultUrl": "https://github.com/onivim/oni", + }) - oni.automation.sendKeysV2("") - await shortDelay() - await simulateTyping("a") - await shortDelay() - await simulateTyping("b") - await shortDelay() + await intro() - oni.automation.sendKeysV2("") + await showKeyboardNavigation() - await simulateTyping(":close") - oni.automation.sendKeysV2("") + await showDevelopment() // --- await showLanguageServices() // --- - await simulateTyping("gT") - - await simulateTyping("o") - await simulateTyping("Enjoy built in search with ripgrep..") - - await pressEscape() - - await openFindInFiles() - await simulateTyping("OniEditor") - await longDelay() - oni.automation.sendKeysV2("") - await longDelay() - - oni.automation.sendKeysV2("") - oni.automation.sendKeysV2("") - - await simulateTyping("o") - await simulateTyping("...or the embedded file finder.") - await shortDelay() - - await pressEscape() - await shortDelay() - await openQuickOpen() - await simulateTyping("NeovimEditor") - await shortDelay() - oni.automation.sendKeysV2("") - await longDelay() - oni.automation.sendKeysV2("") - await shortDelay() - - await simulateTyping("G") - await simulateTyping("o") - await simulateTyping("...use the built in command palette to discover functionality.") - await pressEscape() + // oni.automation.sendKeysV2("") + // oni.automation.sendKeysV2("") + + // await simulateTyping("o") + // await simulateTyping("...or the embedded file finder.") + // await shortDelay() + + // await pressEscape() + // await shortDelay() + // await openQuickOpen() + // await simulateTyping("NeovimEditor") + // await shortDelay() + // oni.automation.sendKeysV2("") + // await longDelay() + // oni.automation.sendKeysV2("") + // await shortDelay() + + // await simulateTyping("G") + // await simulateTyping("o") + // await simulateTyping("...use the built in command palette to discover functionality.") + // await pressEscape() + await showTutorials() await showConfig() + await showComingSoon() await simulateTyping(":q") From 89734cd113d5ba4af5557833a647cd9128a1e605 Mon Sep 17 00:00:00 2001 From: Nick Neisen Date: Tue, 15 May 2018 12:55:03 -0600 Subject: [PATCH 036/102] [RFC] #1999 - Add Targets.vim tutorial (#2192) * Add Targets.vim tutorial Add a tutorial with description, useful keys, and interactive examples to teach users about Target.vim. * Add ci( stages, pauses, and text insertions. Have the user type 'foo' and 'bar' after using the c operator. Change the message to switch to normal mode. Add aditional stages to have the user use the ci( command. Add pauses where needed so that the user can view the result of them entering the correct keys. * Fix spelling mistakes and adjust wordage Change 'paranthesis' missellings to 'parenthesis'. Change command vocabulary to match command. --- .../src/Services/Learning/Tutorial/Notes.tsx | 72 +++++++++ .../Tutorials/TargetsVimPluginTutorial.tsx | 153 ++++++++++++++++++ .../Learning/Tutorial/Tutorials/index.tsx | 2 + 3 files changed, 227 insertions(+) create mode 100644 browser/src/Services/Learning/Tutorial/Tutorials/TargetsVimPluginTutorial.tsx diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index 7f447efda6..a056f3575e 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -362,3 +362,75 @@ export const VisualLineModeKey = (): JSX.Element => { /> ) } + +export const Targetckey = (): JSX.Element => { + return ( + Delete AND INSERT between next pair characters} + /> + ) +} + +export const Targetdkey = (): JSX.Element => { + return ( + Delete between next pair characters} + /> + ) +} + +export const Targetikey = (): JSX.Element => { + return ( + Select first character inside of pair characters} + /> + ) +} + +export const Targetakey = (): JSX.Element => { + return ( + Select next pair including the pair characters} + /> + ) +} + +export const TargetIkey = (): JSX.Element => { + return ( + Select contents of pair characters} + /> + ) +} + +export const TargetAkey = (): JSX.Element => { + return ( + Select around the pair characters} + /> + ) +} + +export const Targetnkey = (): JSX.Element => { + return ( + Select the next pair characters} + /> + ) +} + +export const Targetlkey = (): JSX.Element => { + return ( + Select the previous pair characters} + /> + ) +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/TargetsVimPluginTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/TargetsVimPluginTutorial.tsx new file mode 100644 index 0000000000..7bfca9c0e8 --- /dev/null +++ b/browser/src/Services/Learning/Tutorial/Tutorials/TargetsVimPluginTutorial.tsx @@ -0,0 +1,153 @@ +/** + * + * Tutorial that exercises the targets.vim plugin + */ + +import * as React from "react" + +import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" +import * as Notes from "./../Notes" +import * as Stages from "./../Stages" + +const Line1a = "The Targets.vim plugin is very useful!" +const Line1b = "It was created by Christian Wellenbrock." +const Line1c = "Targets.vim adds text objects for additional (operations)." + +const Line2a = "'cin(' changes inside the next pair of (parenthesis)." +const Line2b = "'can(' changes the next pair of (parenthesis)." +const Line2c = "Replacing 'n' with 'l' will change the previous pair." +const Line2d = "Omitting either will change the (current pair) or the (next)." + +const Line3a = "Quote objects can 'also' be used." +const Line3b = '\'cIn"\' changes the first characters inside of " quotes ".' +const Line3c = '\'cAn"\' changes around the "quotes" .' + +const Line4a = "'din,' will delete inside the next list, with, commas." +const Line4b = "Many different seperators are possible." +const Line4c = "Some applications you might consider not_a_list." + +const Line5a = "Replacing the text character with 'a' will find the next programming argument." +const Line5b = "'dina' will select inside the (next, argument)." +const Line5c = "'dana' deletes the (next, argument)" + +const Line6a = "These are only a brief overview of Targets.vim." +const Line6b = "More advanced features can be found on its github repository." +const Line6c = "https://github.com/wellle/targets.vim" + +export class TargetsVimPluginTutorial implements ITutorial { + private _stages: ITutorialStage[] + + constructor() { + this._stages = [ + new Stages.SetBufferStage([Line1a, Line1b, Line1c]), + new Stages.SetCursorPositionStage(0, 0), + new Stages.MoveToGoalStage("Use 'cin(' to change inside the next parenthesis", 2, 46), + new Stages.MoveToGoalStage("Type 'foo'", 2, 49), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + Stages.combine( + null, + new Stages.FadeInLineStage(null, 1, Line2a), + new Stages.FadeInLineStage(null, 2, Line2b), + new Stages.FadeInLineStage(null, 3, Line2c), + new Stages.SetBufferStage([Line2a, Line2b, Line2c, Line2d]), + ), + new Stages.SetCursorPositionStage(0, 7), + new Stages.MoveToGoalStage("Use 'cin(' to change inside the next parenthesis", 0, 40), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.SetCursorPositionStage(1, 7), + new Stages.MoveToGoalStage("Use 'can(' to change the next parenthesis", 1, 32), + new Stages.MoveToGoalStage("Type 'bar'", 1, 35), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.MoveToGoalStage("Use 'cil(' to change inside the last parenthesis", 0, 40), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.SetCursorPositionStage(3, 34), + new Stages.MoveToGoalStage("Use 'ca(' to change the current parenthesis", 3, 32), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.MoveToGoalStage("Use 'ca(' to change the next parenthesis", 3, 40), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + Stages.combine( + null, + new Stages.FadeInLineStage(null, 1, Line3a), + new Stages.FadeInLineStage(null, 2, Line3b), + new Stages.SetBufferStage([Line3a, Line3b, Line3c]), + ), + new Stages.SetCursorPositionStage(0, 0), + new Stages.MoveToGoalStage("Use 'cin'' to change inside the next single quotes", 0, 19), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.SetCursorPositionStage(1, 7), + new Stages.MoveToGoalStage( + "Use 'cIn\"' to change the first character inside the next double quotes", + 1, + 48, + ), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.SetCursorPositionStage(2, 7), + new Stages.MoveToGoalStage("Use 'cAn\"' to change the next double quotes", 2, 26), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + Stages.combine( + null, + new Stages.FadeInLineStage(null, 1, Line4a), + new Stages.FadeInLineStage(null, 2, Line4b), + new Stages.SetBufferStage([Line4a, Line4b, Line4c]), + ), + new Stages.SetCursorPositionStage(0, 7), + new Stages.MoveToGoalStage("Use 'din,' to delete the next item in the list", 0, 40), + new Stages.MoveToGoalStage( + "Use 'din_' to delete inside the next underline in the variable", + 2, + 41, + ), + new Stages.WaitForModeStage("Press ESC to go back to normal mode", "normal"), + new Stages.MoveToGoalStage("Type gg to go to the beginning.", 0, 0), + Stages.combine( + null, + new Stages.FadeInLineStage(null, 1, Line5a), + new Stages.FadeInLineStage(null, 2, Line5b), + new Stages.SetBufferStage([Line5a, Line5b, Line5c]), + ), + new Stages.SetCursorPositionStage(0, 0), + new Stages.MoveToGoalStage( + "Use 'dina' to delete inside the next programming argument", + 1, + 31, + ), + new Stages.MoveToGoalStage("Use 'dana' to delete the next argument", 2, 20), + new Stages.MoveToGoalStage("Type gg to go to the beginning.", 0, 0), + Stages.combine( + null, + new Stages.FadeInLineStage(null, 1, Line6a), + new Stages.FadeInLineStage(null, 2, Line6b), + new Stages.SetBufferStage([Line6a, Line6b, Line6c]), + ), + new Stages.SetCursorPositionStage(2, 0), + new Stages.MoveToGoalStage("Type gg to go to the beginning.", 0, 0), + ] + } + + public get metadata(): ITutorialMetadata { + return { + id: "oni.tutorials.targets_plugin", + name: "Target.vim plugin", + description: + 'Target.vim is a plugin installed by default to help move between pairs of characters such as (), {}, or "". It does this by adding various text objects to operate on and expand simple commands like \'di"\'.', + level: 300, + } + } + + public get stages(): ITutorialStage[] { + return this._stages + } + + public get notes(): JSX.Element[] { + return [ + , + , + , + , + , + , + , + , + ] + } +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx index 44136f7da1..64a14d8117 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx @@ -13,6 +13,7 @@ import { DeleteOperatorTutorial } from "./DeleteOperatorTutorial" import { MoveAndInsertTutorial } from "./MoveAndInsertTutorial" import { SearchInBufferTutorial } from "./SearchInBufferTutorial" import { SwitchModeTutorial } from "./SwitchModeTutorial" +import { TargetsVimPluginTutorial } from "./TargetsVimPluginTutorial" import { VerticalMovementTutorial } from "./VerticalMovementTutorial" import { VisualModeTutorial } from "./VisualModeTutorial" import { WordMotionTutorial } from "./WordMotionTutorial" @@ -33,4 +34,5 @@ export const AllTutorials: ITutorial[] = [ new CopyPasteTutorial(), new ChangeOperatorTutorial(), new VisualModeTutorial(), + new TargetsVimPluginTutorial(), ] From 61e6b53a7b140741762271f35c53c68317b63cec Mon Sep 17 00:00:00 2001 From: keforbes Date: Tue, 15 May 2018 14:57:52 -0600 Subject: [PATCH 037/102] enable sidebar.width config setting --- .../src/Services/Configuration/DefaultConfiguration.ts | 2 +- browser/src/Services/Sidebar/SidebarContentSplit.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/browser/src/Services/Configuration/DefaultConfiguration.ts b/browser/src/Services/Configuration/DefaultConfiguration.ts index f170c6c36a..a631e8c4a9 100644 --- a/browser/src/Services/Configuration/DefaultConfiguration.ts +++ b/browser/src/Services/Configuration/DefaultConfiguration.ts @@ -376,7 +376,7 @@ const BaseConfiguration: IConfigurationValues = { "sidebar.enabled": true, "sidebar.default.open": true, - "sidebar.width": "50px", + "sidebar.width": "15em", "sidebar.marks.enabled": false, "sidebar.plugins.enabled": false, diff --git a/browser/src/Services/Sidebar/SidebarContentSplit.tsx b/browser/src/Services/Sidebar/SidebarContentSplit.tsx index 7720da8083..9831cfd508 100644 --- a/browser/src/Services/Sidebar/SidebarContentSplit.tsx +++ b/browser/src/Services/Sidebar/SidebarContentSplit.tsx @@ -8,6 +8,7 @@ import styled, { keyframes } from "styled-components" import { Event, IDisposable, IEvent } from "oni-types" +import { configuration } from "../Configuration" import { enableMouse, withProps } from "./../../UI/components/common" import { ISidebarEntry, ISidebarState, SidebarManager, SidebarPane } from "./SidebarStore" @@ -82,7 +83,7 @@ const EntranceKeyframes = keyframes` export const SidebarContentWrapper = withProps<{}>(styled.div)` ${enableMouse} - width: 200px; + width: ${props => props.width}; color: ${props => props.theme["editor.foreground"]}; background-color: ${props => props.theme["editor.background"]}; height: 100%; @@ -173,7 +174,10 @@ export class SidebarContentView extends React.PureComponent< const header = activeEntry && activeEntry.pane ? activeEntry.pane.title : null return ( - + {activeEntry.pane.render()} From dd417ae184babcb51081c2446a34f167733294a5 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 08:18:06 -0700 Subject: [PATCH 038/102] Limit chords to four keys (#2208) --- browser/src/Services/InputManager.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/InputManager.ts b/browser/src/Services/InputManager.ts index 2d188b7907..89daa64858 100644 --- a/browser/src/Services/InputManager.ts +++ b/browser/src/Services/InputManager.ts @@ -20,6 +20,7 @@ export interface KeyBindingMap { } const MAX_DELAY_BETWEEN_KEY_CHORD = 250 /* milliseconds */ +const MAX_CHORD_SIZE = 4 import { KeyboardResolver } from "./../Input/Keyboard/KeyboardResolver" @@ -39,7 +40,7 @@ export const getRecentKeyPresses = ( keys: KeyPressInfo[], maxTimeBetweenKeyPresses: number, ): KeyPressInfo[] => { - return keys.reduce( + const chords = keys.reduce( (prev, curr) => { if (prev.length === 0) { return [curr] @@ -55,6 +56,8 @@ export const getRecentKeyPresses = ( }, [] as KeyPressInfo[], ) + + return chords.slice(0, MAX_CHORD_SIZE) } export class InputManager implements Oni.Input.InputManager { From 0ce4fb199391ae725962acad2c82d2cb1f52352b Mon Sep 17 00:00:00 2001 From: Ryan C Date: Wed, 16 May 2018 16:19:24 +0100 Subject: [PATCH 039/102] Moved Issue template to the correct folder. (#2212) * Moved Issue template to the correct folder. * Fix template. --- .github/ISSUE_TEMPLATE.md | 12 ++++++++++++ ISSUE_TEMPLATE.md | 11 ----------- 2 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index b9ff69a5fd..64977ba765 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,2 +1,14 @@ + +Oni Version: +Neovim Version (Linux only): +Operating System: + +#### Describe your issue + +#### Expected behaviour + +#### Actual behaviour + +#### Steps to reproduce diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 79e0b61b6b..0000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -- oni version: -- neovim version (linux only): -- operating system: - -### Describe your issue - -### Expected behaviour - -### Actual behaviour - -### Steps to reproduce From 94d6cbfc384d3e73a7fe3d2bf91d35b8fb7bbe12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Griepenburg?= Date: Thu, 17 May 2018 01:27:03 +1000 Subject: [PATCH 040/102] Enable use of middle click to close tabs (#2190) * Add middle click tab closing (#2069) by adding a onMouseDown directive to both tab file icon and tab name and checking the resulting React.MouseEvent for a middle click * Fix unused imports and whitespace in Tabs ui test (#2069) * Update Tabs component test snapshot (#2069) * Fix Tabs ui test to correctly retrieve children (#2069) * Differentiate between single und multiple tabs in test (#2069) * Use mousedown event for tab selection and closing (#2069) by middle clicking and let the tab itself handle the logic for it by checking React.MouseEvent.button value * Update classnames dependency to lastest master branch and write own @types module declaration so that it can be used in testing nstead of using a mock. The mock does not suffice as the way the actual module previously had to be imported was "import * as classNames" where classNames was the actual function. It is not possible to build a module in typescript/es6 imports, which will directly return a function in the same way the dependency did in commonjs module syntax. Instead when defining a function to be returned as default export it is returned as an Object like this "{ default: [Function] }", which is correctly resolved when importing with "import classNames from 'classnames'". This only previously worked in production as webpacks ts-loader, handles this issue, whereas when testing the sources are only compiled with tsc. There is an update to the classnames dependency on the current master, but there hasn't been a release since 2006. So options were to setup webpack for tests as well or add updated classnames dependency which sets a "default" value on its commonjs exports. Links for reference: https://github.com/JedWatson/classnames/issues/152 https://github.com/JedWatson/classnames/pull/106 https://github.com/DefinitelyTyped/DefinitelyTyped/pull/25206 https://github.com/Microsoft/TypeScript/issues/2719 * Fix tab click onMouseDown callback * Test tab clicks to select/close trigger callbacks * Mock child react components directly to test smaller unit * Reset calls to callback mocks in each test case * Add tests for tabs interaction with FileIcon/Sneakable --- @types/classnames/index.d.ts | 20 +++ browser/src/UI/components/Tabs.tsx | 70 ++++---- browser/tsconfig.json | 2 +- browser/tsconfig.test.json | 2 +- jest.config.js | 1 - package.json | 3 +- ui-tests/Tabs.test.tsx | 210 ++++++++++++++++++---- ui-tests/__snapshots__/Tabs.test.tsx.snap | 8 +- ui-tests/mocks/classnames.ts | 2 - ui-tests/tsconfig.react.json | 8 +- yarn.lock | 8 +- 11 files changed, 249 insertions(+), 85 deletions(-) create mode 100644 @types/classnames/index.d.ts delete mode 100644 ui-tests/mocks/classnames.ts diff --git a/@types/classnames/index.d.ts b/@types/classnames/index.d.ts new file mode 100644 index 0000000000..61e25b6a66 --- /dev/null +++ b/@types/classnames/index.d.ts @@ -0,0 +1,20 @@ +// Custom type definitions for classnames master branch +// Project: https://github.com/JedWatson/classnames + +declare module "classnames" { + type ClassValue = string | number | ClassDictionary | ClassArray | undefined | null | false + + interface ClassDictionary { + [id: string]: boolean | undefined | null + } + + // This is the only way I found to break circular references between ClassArray and ClassValue + // https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 + interface ClassArray extends Array {} // tslint:disable-line no-empty-interface + + type ClassNamesFn = (...classes: ClassValue[]) => string + + const classNames: ClassNamesFn + + export default classNames +} diff --git a/browser/src/UI/components/Tabs.tsx b/browser/src/UI/components/Tabs.tsx index e3c321dc66..2d99bda599 100644 --- a/browser/src/UI/components/Tabs.tsx +++ b/browser/src/UI/components/Tabs.tsx @@ -7,7 +7,7 @@ import * as path from "path" import * as React from "react" import { connect } from "react-redux" -import * as classNames from "classnames" +import classNames from "classnames" import { keyframes } from "styled-components" import * as BufferSelectors from "./../../Editor/NeovimEditor/NeovimEditorSelectors" @@ -40,8 +40,8 @@ export interface ITabContainerProps { } export interface ITabsProps { - onSelect?: (id: number) => void - onClose?: (id: number) => void + onTabSelect?: (id: number) => void + onTabClose?: (id: number) => void visible: boolean tabs: ITabProps[] @@ -65,16 +65,6 @@ const InnerName = styled.span` ` export class Tabs extends React.PureComponent { - private _boundOnSelect: (tabId: number) => void - private _boundOnClose: (tabId: number) => void - - constructor(props: ITabsProps) { - super(props) - - this._boundOnSelect = (id: number) => this._onSelect(id) - this._boundOnClose = (id: number) => this._onClickClose(id) - } - public render(): JSX.Element { if (!this.props.visible) { return null @@ -98,8 +88,8 @@ export class Tabs extends React.PureComponent { {
) } - - private _onSelect(id: number): void { - this.props.onSelect(id) - } - - private _onClickClose(id: number): void { - this.props.onClose(id) - } } export interface ITabPropsWithClick extends ITabProps { - onClickName: (id: number) => void - onClickClose: (id: number) => void + onSelect: (id: number) => void + onClose: (id: number) => void backgroundColor: string foregroundColor: string @@ -175,28 +157,28 @@ export class Tab extends React.PureComponent { borderTop: "2px solid " + this.props.highlightColor, } + const handleTitleClick = this._handleTitleClick.bind(this) + const handleCloseButtonClick = this._handleCloseButtonClick.bind(this) + return ( - this.props.onClickName(this.props.id)} tag={this.props.name}> + this.props.onSelect(this.props.id)} tag={this.props.name}> (this._tab = e)} className={cssClasses} title={this.props.description} style={style} > -
this.props.onClickName(this.props.id)}> +
-
this.props.onClickName(this.props.id)}> +
{this.props.name}
-
this.props.onClickClose(this.props.id)} - > +
@@ -220,6 +202,26 @@ export class Tab extends React.PureComponent { } } } + + private _handleTitleClick(event: React.MouseEvent): void { + if (this._isLeftClick(event)) { + this.props.onSelect(this.props.id) + } else if (this._isMiddleClick(event)) { + this.props.onClose(this.props.id) + } + } + + private _handleCloseButtonClick(): void { + this.props.onClose(this.props.id) + } + + private _isMiddleClick(event: React.MouseEvent): boolean { + return event.button === 1 + } + + private _isLeftClick(event: React.MouseEvent): boolean { + return event.button === 0 + } } export const getTabName = (name: string, isDuplicate?: boolean): string => { @@ -365,8 +367,8 @@ const mapStateToProps = (state: State.IState, ownProps: ITabContainerProps): ITa fontSize: addDefaultUnitIfNeeded(state.configuration["ui.fontSize"]), backgroundColor: state.colors["tabs.background"], foregroundColor: state.colors["tabs.foreground"], - onSelect: selectFunc, - onClose: closeFunc, + onTabSelect: selectFunc, + onTabClose: closeFunc, height, maxWidth, shouldWrap, diff --git a/browser/tsconfig.json b/browser/tsconfig.json index d8157e6cc0..c2be110923 100644 --- a/browser/tsconfig.json +++ b/browser/tsconfig.json @@ -26,6 +26,6 @@ "sourceMap": true, "types": ["mocha", "webgl2"] }, - "include": ["src/**/*.ts", "test/**/*.ts"], + "include": ["../@types/**/*.d.ts", "src/**/*.ts", "test/**/*.ts"], "exclude": ["node_modules"] } diff --git a/browser/tsconfig.test.json b/browser/tsconfig.test.json index f2488b6c90..ce0db2c4e5 100644 --- a/browser/tsconfig.test.json +++ b/browser/tsconfig.test.json @@ -22,6 +22,6 @@ "sourceMap": true, "types": ["mocha", "webgl2"] }, - "include": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts"], + "include": ["../@types/**/*.d.ts", "src/**/*.ts", "src/**/*.tsx", "test/**/*.ts"], "exclude": ["node_modules"] } diff --git a/jest.config.js b/jest.config.js index b2dd906656..1ab99e87ed 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,6 @@ module.exports = { PersistentSettings: "/ui-tests/mocks/PersistentSettings.ts", Utility: "/ui-tests/mocks/Utility.ts", Configuration: "/ui-tests/mocks/Configuration.ts", - classnames: "/ui-tests/mocks/classnames.ts", KeyboardLayout: "/ui-tests/mocks/keyboardLayout.ts", }, snapshotSerializers: ["enzyme-to-json/serializer"], diff --git a/package.json b/package.json index 8337a0061f..5d0919bedb 100644 --- a/package.json +++ b/package.json @@ -720,7 +720,6 @@ }, "devDependencies": { "@types/chokidar": "^1.7.5", - "@types/classnames": "0.0.32", "@types/color": "2.0.0", "@types/detect-indent": "^5.0.0", "@types/dompurify": "^0.0.31", @@ -759,7 +758,7 @@ "babel-minify-webpack-plugin": "^0.3.1", "babel-plugin-dynamic-import-node": "^1.2.0", "bs-platform": "2.1.0", - "classnames": "2.2.5", + "classnames": "JedWatson/classnames", "codecov": "^3.0.0", "color": "2.0.0", "concurrently": "3.1.0", diff --git a/ui-tests/Tabs.test.tsx b/ui-tests/Tabs.test.tsx index 06740afb43..bbb3261cfb 100644 --- a/ui-tests/Tabs.test.tsx +++ b/ui-tests/Tabs.test.tsx @@ -2,21 +2,59 @@ import { mount, shallow } from "enzyme" import { shallowToJson } from "enzyme-to-json" import * as React from "react" +jest.mock("../browser/src/Services/FileIcon") +import { FileIcon } from "../browser/src/Services/FileIcon" +jest.mock("../browser/src/UI/components/Sneakable") +import { Sneakable } from "../browser/src/UI/components/Sneakable" + import { Tab, Tabs } from "../browser/src/UI/components/Tabs" describe(" Tests", () => { - const testTabs = [ - { - id: 2, - name: "test", - description: "a test tab", - isSelected: true, - isDirty: true, - iconFileName: "icon", - highlightColor: "#000", - }, - ] - const TestTabs = ( + FileIcon.mockImplementation(() => ({ render: jest.fn() })) + Sneakable.mockImplementation((props) => ({ + render: () => props.children, + })) + + const tabCloseFunction = jest.fn() + const tabSelectFunction = jest.fn() + + const tab1 = { + id: 1, + name: "test", + description: "a test tab", + isSelected: true, + isDirty: true, + iconFileName: "icon", + highlightColor: "#000", + } + + const tab2 = { + id: 2, + name: "test", + description: "a test tab", + isSelected: false, + isDirty: true, + iconFileName: "icon", + highlightColor: "#000", + } + + const TabsContainingSingleTab = ( + + ) + + const TabsContainingTwoTabs = ( Tests", () => { foregroundColor="#000" shouldWrap={false} visible={true} - tabs={testTabs} + onTabClose={tabCloseFunction} + onTabSelect={tabSelectFunction} + tabs={[tab1, tab2]} /> ) + + const TabsNotVisible = ( + + ) + + afterEach(() => { + tabCloseFunction.mockReset() + tabSelectFunction.mockReset() + }) + it("renders without crashing", () => { - const wrapper = shallow(TestTabs) + const wrapper = shallow(TabsContainingSingleTab) expect(wrapper.length).toEqual(1) }) + it("should match last known snapshot unless we make a change", () => { - const wrapper = shallow(TestTabs) + const wrapper = shallow(TabsContainingSingleTab) expect(shallowToJson(wrapper)).toMatchSnapshot() }) - it("Should render the correct number of tabs", () => { - const wrapper = shallow(TestTabs) - expect(wrapper.children.length).toEqual(1) - }) - it("Should not render if the visible prop is false", () => { - const wrapper = shallow( - , - ) + + it("should render the correct number of tabs", () => { + expect(shallow(TabsContainingSingleTab).children().length).toEqual(1) + expect(shallow(TabsContainingTwoTabs).children().length).toEqual(2) + }) + + it("should not render if the visible prop is false", () => { + const wrapper = shallow(TabsNotVisible) expect(wrapper.getElement()).toBe(null) }) + + it("should call onTabClose callback on tab close button click", () => { + const wrapper = mount(TabsContainingSingleTab) + const clickedTab = wrapper.find(Tab) + + wrapper + .find(".corner") + .last() + .simulate("click") + + expect(tabCloseFunction).toHaveBeenCalledWith(clickedTab.props().id) + + wrapper.unmount() + }) + + it("should call onTabSelect callback on tab title click", () => { + const wrapper = mount(TabsContainingTwoTabs) + const clickedTab = wrapper.find(Tab).last() + + clickedTab.find(".name").simulate("mouseDown", { button: 0 }) + expect(tabSelectFunction).toHaveBeenCalledWith(clickedTab.props().id) + + wrapper.unmount() + }) + + it("should call onTabClose callback on tab title middle click", () => { + const wrapper = mount(TabsContainingTwoTabs) + const clickedTab = wrapper.find(Tab).first() + + clickedTab.find(".name").simulate("mouseDown", { button: 1 }) + expect(tabCloseFunction).toHaveBeenCalledWith(clickedTab.props().id) + + wrapper.unmount() + }) + + it("should call onTabSelect callback on tab file icon click", () => { + const wrapper = mount(TabsContainingTwoTabs) + const clickedTab = wrapper.find(Tab).last() + + clickedTab + .find(".corner") + .first() + .simulate("mouseDown", { button: 0 }) + expect(tabSelectFunction).toHaveBeenCalledWith(clickedTab.props().id) + + wrapper.unmount() + }) + + it("should call onTabClose callback on tab file icon middle click", () => { + const wrapper = mount(TabsContainingTwoTabs) + const clickedTab = wrapper.find(Tab).first() + + clickedTab + .find(".corner") + .first() + .simulate("mouseDown", { button: 1 }) + expect(tabCloseFunction).toHaveBeenCalledWith(clickedTab.props().id) + + wrapper.unmount() + }) + + it("should pass tab name as Sneakable tag property", () => { + const wrapper = mount(TabsContainingSingleTab) + const tab = wrapper.find(Tab) + const sneakable = wrapper.find(Sneakable) + + expect(sneakable.props().tag).toEqual(tab.props().name) + + wrapper.unmount() + }); + + it("should call onTabSelect callback as Sneakable callback", () => { + Sneakable.mockImplementationOnce((props) => { + props.callback() + } + + const wrapper = mount(TabsContainingSingleTab) + const sneakableTab = wrapper.find(Tab) + + expect(tabSelectFunction).toHaveBeenCalledWith(sneakableTab.props().id) + + wrapper.unmount() + }); + + it("should pass tab icon file name as FileIcon fileName property ", () => { + const wrapper = mount(TabsContainingSingleTab) + const tab = wrapper.find(Tab) + const fileIcon = wrapper.find(FileIcon) + + expect(fileIcon.props().fileName).toEqual(tab.props().iconFileName) + + wrapper.unmount() + }); }) diff --git a/ui-tests/__snapshots__/Tabs.test.tsx.snap b/ui-tests/__snapshots__/Tabs.test.tsx.snap index ae38c754de..6275ee24b6 100644 --- a/ui-tests/__snapshots__/Tabs.test.tsx.snap +++ b/ui-tests/__snapshots__/Tabs.test.tsx.snap @@ -18,14 +18,14 @@ exports[` Tests should match last known snapshot unless we make a change height="2em" highlightColor="#000" iconFileName="icon" - id={2} + id={1} isDirty={true} isSelected={true} - key="2" + key="1" maxWidth="20em" name="test" - onClickClose={[Function]} - onClickName={[Function]} + onClose={[MockFunction]} + onSelect={[MockFunction]} />
`; diff --git a/ui-tests/mocks/classnames.ts b/ui-tests/mocks/classnames.ts deleted file mode 100644 index 33abc85533..0000000000 --- a/ui-tests/mocks/classnames.ts +++ /dev/null @@ -1,2 +0,0 @@ -export default (component: string, classes: { [key: string]: string }) => jest.fn() -export const classNames = jest.fn() diff --git a/ui-tests/tsconfig.react.json b/ui-tests/tsconfig.react.json index 3621a32087..1364905d4e 100644 --- a/ui-tests/tsconfig.react.json +++ b/ui-tests/tsconfig.react.json @@ -22,6 +22,12 @@ "sourceMap": true, "types": ["jest", "electron", "react", "webgl2"] }, - "include": ["**/*.tsx", "**/*.ts", "../browser/**/*.ts", "../browser/**/*.tsx"], + "include": [ + "**/*.tsx", + "**/*.ts", + "../@types/**/*.d.ts", + "../browser/**/*.ts", + "../browser/**/*.tsx" + ], "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index 76930e7132..c75d5ee8dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -55,10 +55,6 @@ "@types/events" "*" "@types/node" "*" -"@types/classnames@0.0.32": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-0.0.32.tgz#449abcd9a826807811ef101e58df9f83cfc61713" - "@types/color-convert@*": version "1.9.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d" @@ -2243,9 +2239,9 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@2.2.5, classnames@^2.2.3, classnames@^2.2.5: +classnames@JedWatson/classnames, classnames@^2.2.3, classnames@^2.2.5: version "2.2.5" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" + resolved "https://codeload.github.com/JedWatson/classnames/tar.gz/34a05a53d31d35879ec7088949ae5a1b2224043a" clean-css@4.1.x: version "4.1.11" From c43cea6da1d079a434b0f526cc2fffb2e65c56c8 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 10:55:55 -0700 Subject: [PATCH 041/102] Add Domenico Maisano as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 6bb63e549d..dc651dcf8b 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -119,6 +119,7 @@ Thanks you to all our backers for making Oni possible! * Kim Fiedler * Nicolaus Hepler * Nick Price +* Domenico Maisano From d608b53c147a58c703c1babde26228cb4fad0831 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 10:56:13 -0700 Subject: [PATCH 042/102] Add Daniel Polanco as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index dc651dcf8b..d0a4beb2e6 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -120,6 +120,7 @@ Thanks you to all our backers for making Oni possible! * Nicolaus Hepler * Nick Price * Domenico Maisano +* Daniel Polanco From e055a07fb7eb06ae6869df7aae836f78214c4cd9 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 10:56:43 -0700 Subject: [PATCH 043/102] Add Eric Hall as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index d0a4beb2e6..cae951fec1 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -121,6 +121,7 @@ Thanks you to all our backers for making Oni possible! * Nick Price * Domenico Maisano * Daniel Polanco +* Eric Hall From 30865a66396103e2d8367acf652d16d0f120d877 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 10:57:17 -0700 Subject: [PATCH 044/102] Add Dimas Cyriaco as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index cae951fec1..b820c5b606 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -122,6 +122,7 @@ Thanks you to all our backers for making Oni possible! * Domenico Maisano * Daniel Polanco * Eric Hall +* Dimas Cyriaco From e80fb44a2d96018df94e3be6071e40d0d9c2081c Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 10:58:53 -0700 Subject: [PATCH 045/102] Add Carlos Coves Prieto as a backer - thank you! :) --- .github/config.yml | 2 +- BACKERS.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/config.yml b/.github/config.yml index af80e47128..ac06da58b8 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -32,4 +32,4 @@ backers: - 14060883 - 244396 - 8832878 - +- 5127194 diff --git a/BACKERS.md b/BACKERS.md index b820c5b606..8cf5fd5c50 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -123,6 +123,7 @@ Thanks you to all our backers for making Oni possible! * Daniel Polanco * Eric Hall * Dimas Cyriaco +* Carlos Coves Prieto From 8766bf2b41f4db61a8fdce80296234e8eb105653 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 15:42:54 -0700 Subject: [PATCH 046/102] Feature - Buffer Layers: Add 'visibleLines' to the context for buffer layers (#2188) * Plumb through 'visible lines' to the buffer layer context * Fix lint issues * Augment the AddLayer test to validate we are getting content out * Upgrade to latest oni-api and bring in improved types --- .../NeovimEditor/NeovimBufferLayersView.tsx | 7 +- .../src/Editor/NeovimEditor/NeovimEditor.tsx | 1 + .../NeovimEditor/NeovimEditorActions.ts | 15 + .../NeovimEditor/NeovimEditorReducer.ts | 2 + .../Editor/NeovimEditor/NeovimEditorStore.ts | 3 + browser/src/neovim/NeovimWindowManager.ts | 3 + package.json | 382 +++++++++++++----- test/ci/Api.Buffer.AddLayer.tsx | 9 +- yarn.lock | 6 +- 9 files changed, 311 insertions(+), 117 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx b/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx index 3f49d3d24f..e4013ef4b8 100644 --- a/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimBufferLayersView.tsx @@ -40,15 +40,18 @@ export class NeovimBufferLayersView extends React.PureComponent { diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index d5f08f95e3..d4f3a22415 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -398,6 +398,7 @@ export class NeovimEditor extends Editor implements IEditor { activeWindow.topBufferLine, activeWindow.dimensions, activeWindow.bufferToScreen, + activeWindow.visibleLines, ) } diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts b/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts index 87c010f742..fc8f7b3849 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts @@ -225,9 +225,11 @@ export interface ISetWindowState { bufferToScreen: Oni.Coordinates.BufferToScreen screenToPixel: Oni.Coordinates.ScreenToPixel + bufferToPixel: Oni.Coordinates.BufferToPixel topBufferLine: number bottomBufferLine: number + visibleLines: string[] } } @@ -526,6 +528,7 @@ export const setWindowState = ( topBufferLine: number, dimensions: Oni.Shapes.Rectangle, bufferToScreen: Oni.Coordinates.BufferToScreen, + visibleLines: string[], ) => (dispatch: DispatchFunction, getState: GetStateFunction) => { const { fontPixelWidth, fontPixelHeight } = getState() @@ -547,6 +550,16 @@ export const setWindowState = ( } } + const bufferToPixel = (position: types.Position): Oni.Coordinates.PixelSpacePoint => { + const screenPosition = bufferToScreen(position) + + if (!screenPosition) { + return null + } + + return screenToPixel(screenPosition) + } + dispatch({ type: "SET_WINDOW_STATE", payload: { @@ -558,8 +571,10 @@ export const setWindowState = ( line, bufferToScreen, screenToPixel, + bufferToPixel, bottomBufferLine, topBufferLine, + visibleLines, }, }) } diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts b/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts index a66a24721d..81b9e2ea1a 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts @@ -413,10 +413,12 @@ export const windowStateReducer = ( line: a.payload.line, bufferToScreen: a.payload.bufferToScreen, screenToPixel: a.payload.screenToPixel, + bufferToPixel: a.payload.bufferToPixel, dimensions: a.payload.dimensions, topBufferLine: a.payload.topBufferLine, bottomBufferLine: a.payload.bottomBufferLine, + visibleLines: a.payload.visibleLines, }, }, } diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts b/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts index db9800ca92..3b57ef2664 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts @@ -159,10 +159,13 @@ export interface IWindow { bufferToScreen: Oni.Coordinates.BufferToScreen screenToPixel: Oni.Coordinates.ScreenToPixel + bufferToPixel: Oni.Coordinates.BufferToPixel dimensions: Oni.Shapes.Rectangle topBufferLine: number bottomBufferLine: number + + visibleLines: string[] } export function readConf( diff --git a/browser/src/neovim/NeovimWindowManager.ts b/browser/src/neovim/NeovimWindowManager.ts index 2f063a3756..2aefac48ee 100644 --- a/browser/src/neovim/NeovimWindowManager.ts +++ b/browser/src/neovim/NeovimWindowManager.ts @@ -39,6 +39,8 @@ export interface NeovimActiveWindowState { topBufferLine: number bottomBufferLine: number + visibleLines: string[] + bufferToScreen: Oni.Coordinates.BufferToScreen dimensions: Oni.Shapes.Rectangle } @@ -234,6 +236,7 @@ export class NeovimWindowManager extends Utility.Disposable { bottomBufferLine: context.windowBottomLine - 1, topBufferLine: context.windowTopLine, dimensions, + visibleLines: lines || [], bufferToScreen: getBufferToScreenFromRanges(offset, expandedWidthRanges), } diff --git a/package.json b/package.json index 5d0919bedb..c32d51307f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,14 @@ "homepage": "https://www.onivim.io", "version": "0.3.5", "description": "Code editor with a modern twist on modal editing - powered by neovim.", - "keywords": ["vim", "neovim", "text", "editor", "ide", "vim"], + "keywords": [ + "vim", + "neovim", + "text", + "editor", + "ide", + "vim" + ], "main": "./lib/main/src/main.js", "bin": { "oni": "./cli/oni", @@ -44,23 +51,39 @@ "mac": { "artifactName": "${productName}-${version}-osx.${ext}", "category": "public.app-category.developer-tools", - "target": ["dmg"], - "files": ["bin/osx/**/*"] + "target": [ + "dmg" + ], + "files": [ + "bin/osx/**/*" + ] }, "linux": { "artifactName": "${productName}-${version}-${arch}-linux.${ext}", "maintainer": "bryphe@outlook.com", - "target": ["tar.gz", "deb", "rpm"] + "target": [ + "tar.gz", + "deb", + "rpm" + ] }, "win": { - "target": ["zip", "dir"], - "files": ["bin/x86/**/*"] + "target": [ + "zip", + "dir" + ], + "files": [ + "bin/x86/**/*" + ] }, "fileAssociations": [ { "name": "ADA source", "role": "Editor", - "ext": ["adb", "ads"] + "ext": [ + "adb", + "ads" + ] }, { "name": "Compiled AppleScript", @@ -80,12 +103,20 @@ { "name": "ASP document", "role": "Editor", - "ext": ["asp", "asa"] + "ext": [ + "asp", + "asa" + ] }, { "name": "ASP.NET document", "role": "Editor", - "ext": ["aspx", "ascx", "asmx", "ashx"] + "ext": [ + "aspx", + "ascx", + "asmx", + "ashx" + ] }, { "name": "BibTeX bibliography", @@ -100,7 +131,13 @@ { "name": "C++ source", "role": "Editor", - "ext": ["cc", "cp", "cpp", "cxx", "c++"] + "ext": [ + "cc", + "cp", + "cpp", + "cxx", + "c++" + ] }, { "name": "C# source", @@ -125,7 +162,10 @@ { "name": "Clojure source", "role": "Editor", - "ext": ["clj", "cljs"] + "ext": [ + "clj", + "cljs" + ] }, { "name": "Comma separated values", @@ -140,12 +180,20 @@ { "name": "CGI script", "role": "Editor", - "ext": ["cgi", "fcgi"] + "ext": [ + "cgi", + "fcgi" + ] }, { "name": "Configuration file", "role": "Editor", - "ext": ["cfg", "conf", "config", "htaccess"] + "ext": [ + "cfg", + "conf", + "config", + "htaccess" + ] }, { "name": "Cascading style sheet", @@ -170,7 +218,10 @@ { "name": "Erlang source", "role": "Editor", - "ext": ["erl", "hrl"] + "ext": [ + "erl", + "hrl" + ] }, { "name": "F-Script source", @@ -180,17 +231,32 @@ { "name": "Fortran source", "role": "Editor", - "ext": ["f", "for", "fpp", "f77", "f90", "f95"] + "ext": [ + "f", + "for", + "fpp", + "f77", + "f90", + "f95" + ] }, { "name": "Header", "role": "Editor", - "ext": ["h", "pch"] + "ext": [ + "h", + "pch" + ] }, { "name": "C++ header", "role": "Editor", - "ext": ["hh", "hpp", "hxx", "h++"] + "ext": [ + "hh", + "hpp", + "hxx", + "h++" + ] }, { "name": "Go source", @@ -200,17 +266,28 @@ { "name": "GTD document", "role": "Editor", - "ext": ["gtd", "gtdlog"] + "ext": [ + "gtd", + "gtdlog" + ] }, { "name": "Haskell source", "role": "Editor", - "ext": ["hs", "lhs"] + "ext": [ + "hs", + "lhs" + ] }, { "name": "HTML document", "role": "Editor", - "ext": ["htm", "html", "phtml", "shtml"] + "ext": [ + "htm", + "html", + "phtml", + "shtml" + ] }, { "name": "Include file", @@ -250,7 +327,10 @@ { "name": "JavaScript source", "role": "Editor", - "ext": ["js", "htc"] + "ext": [ + "js", + "htc" + ] }, { "name": "Java Server Page", @@ -275,7 +355,14 @@ { "name": "Lisp source", "role": "Editor", - "ext": ["lisp", "cl", "l", "lsp", "mud", "el"] + "ext": [ + "lisp", + "cl", + "l", + "lsp", + "mud", + "el" + ] }, { "name": "Log file", @@ -295,7 +382,12 @@ { "name": "Markdown document", "role": "Editor", - "ext": ["markdown", "mdown", "markdn", "md"] + "ext": [ + "markdown", + "mdown", + "markdn", + "md" + ] }, { "name": "Makefile source", @@ -305,17 +397,29 @@ { "name": "Mediawiki document", "role": "Editor", - "ext": ["wiki", "wikipedia", "mediawiki"] + "ext": [ + "wiki", + "wikipedia", + "mediawiki" + ] }, { "name": "MIPS assembler source", "role": "Editor", - "ext": ["s", "mips", "spim", "asm"] + "ext": [ + "s", + "mips", + "spim", + "asm" + ] }, { "name": "Modula-3 source", "role": "Editor", - "ext": ["m3", "cm3"] + "ext": [ + "m3", + "cm3" + ] }, { "name": "MoinMoin document", @@ -335,17 +439,28 @@ { "name": "OCaml source", "role": "Editor", - "ext": ["ml", "mli", "mll", "mly"] + "ext": [ + "ml", + "mli", + "mll", + "mly" + ] }, { "name": "Mustache document", "role": "Editor", - "ext": ["mustache", "hbs"] + "ext": [ + "mustache", + "hbs" + ] }, { "name": "Pascal source", "role": "Editor", - "ext": ["pas", "p"] + "ext": [ + "pas", + "p" + ] }, { "name": "Patch file", @@ -355,7 +470,11 @@ { "name": "Perl source", "role": "Editor", - "ext": ["pl", "pod", "perl"] + "ext": [ + "pl", + "pod", + "perl" + ] }, { "name": "Perl module", @@ -365,47 +484,80 @@ { "name": "PHP source", "role": "Editor", - "ext": ["php", "php3", "php4", "php5"] + "ext": [ + "php", + "php3", + "php4", + "php5" + ] }, { "name": "PostScript source", "role": "Editor", - "ext": ["ps", "eps"] + "ext": [ + "ps", + "eps" + ] }, { "name": "Property list", "role": "Editor", - "ext": ["dict", "plist", "scriptSuite", "scriptTerminology"] + "ext": [ + "dict", + "plist", + "scriptSuite", + "scriptTerminology" + ] }, { "name": "Python source", "role": "Editor", - "ext": ["py", "rpy", "cpy", "python"] + "ext": [ + "py", + "rpy", + "cpy", + "python" + ] }, { "name": "R source", "role": "Editor", - "ext": ["r", "s"] + "ext": [ + "r", + "s" + ] }, { "name": "Ragel source", "role": "Editor", - "ext": ["rl", "ragel"] + "ext": [ + "rl", + "ragel" + ] }, { "name": "Remind document", "role": "Editor", - "ext": ["rem", "remind"] + "ext": [ + "rem", + "remind" + ] }, { "name": "reStructuredText document", "role": "Editor", - "ext": ["rst", "rest"] + "ext": [ + "rst", + "rest" + ] }, { "name": "HTML with embedded Ruby", "role": "Editor", - "ext": ["rhtml", "erb"] + "ext": [ + "rhtml", + "erb" + ] }, { "name": "SQL with embedded Ruby", @@ -415,17 +567,28 @@ { "name": "Ruby source", "role": "Editor", - "ext": ["rb", "rbx", "rjs", "rxml"] + "ext": [ + "rb", + "rbx", + "rjs", + "rxml" + ] }, { "name": "Sass source", "role": "Editor", - "ext": ["sass", "scss"] + "ext": [ + "sass", + "scss" + ] }, { "name": "Scheme source", "role": "Editor", - "ext": ["scm", "sch"] + "ext": [ + "scm", + "sch" + ] }, { "name": "Setext document", @@ -473,7 +636,10 @@ { "name": "SWIG source", "role": "Editor", - "ext": ["i", "swg"] + "ext": [ + "i", + "swg" + ] }, { "name": "Tcl source", @@ -483,12 +649,20 @@ { "name": "TeX document", "role": "Editor", - "ext": ["tex", "sty", "cls"] + "ext": [ + "tex", + "sty", + "cls" + ] }, { "name": "Plain text document", "role": "Editor", - "ext": ["text", "txt", "utf8"] + "ext": [ + "text", + "txt", + "utf8" + ] }, { "name": "Textile document", @@ -508,17 +682,32 @@ { "name": "XML document", "role": "Editor", - "ext": ["xml", "xsd", "xib", "rss", "tld", "pt", "cpt", "dtml"] + "ext": [ + "xml", + "xsd", + "xib", + "rss", + "tld", + "pt", + "cpt", + "dtml" + ] }, { "name": "XSL stylesheet", "role": "Editor", - "ext": ["xsl", "xslt"] + "ext": [ + "xsl", + "xslt" + ] }, { "name": "Electronic business card", "role": "Editor", - "ext": ["vcf", "vcard"] + "ext": [ + "vcf", + "vcard" + ] }, { "name": "Visual Basic source", @@ -528,7 +717,10 @@ { "name": "YAML document", "role": "Editor", - "ext": ["yaml", "yml"] + "ext": [ + "yaml", + "yml" + ] }, { "name": "Text document", @@ -590,95 +782,65 @@ "scripts": { "precommit": "pretty-quick --staged", "prepush": "yarn run build && yarn run lint", - "build": - "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins", - "build-debug": - "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins", + "build": "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins", + "build-debug": "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins", "build:browser": "webpack --config browser/webpack.production.config.js", "build:browser-debug": "webpack --config browser/webpack.debug.config.js", "build:main": "cd main && tsc -p tsconfig.json", - "build:plugins": - "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview", + "build:plugins": "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview", "build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && yarn run build", - "build:plugin:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && yarn run build", + "build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn run build", "build:test": "yarn run build:test:unit && yarn run build:test:integration", "build:test:integration": "cd test && tsc -p tsconfig.json", "build:test:unit": "yarn run build:test:unit:browser && yarn run build:test:unit:main", - "build:test:unit:browser": - "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", + "build:test:unit:browser": "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json", "build:test:unit:main": "rimraf lib_test/main && cd main && tsc -p tsconfig.test.json", "build:webview_preload": "cd webview_preload && tsc -p tsconfig.json", "check-cached-binaries": "node build/script/CheckBinariesForBuild.js", "copy-icons": "node build/CopyIcons.js", - "debug:test:unit:browser": - "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", - "demo:screenshot": - "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", - "demo:video": - "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", - "dist:win:x86": - "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", - "dist:win:x64": - "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", - "pack:win": - "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", + "debug:test:unit:browser": "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "demo:screenshot": "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js", + "demo:video": "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js", + "dist:win:x86": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never", + "dist:win:x64": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never", + "pack:win": "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist", "test": "yarn run test:unit && yarn run test:integration", - "test:integration": - "yarn run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", + "test:integration": "yarn run build:test && mocha -t 120000 lib_test/test/CiTests.js --bail", "test:react": "jest --config ./jest.config.js ./ui-tests", "test:react:watch": "jest --config ./jest.config.js ./ui-tests --watch", "test:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage", - "test:unit:browser": - "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", + "test:unit:browser": "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test", "test:unit": "yarn run test:unit:browser && yarn run test:unit:main && yarn run test:react", - "test:unit:main": - "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", + "test:unit:main": "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test", "upload:dist": "node build/script/UploadDistributionBuildsToAzure", "fix-lint": "yarn run fix-lint:browser && yarn run fix-lint:main && yarn run fix-lint:test", - "fix-lint:browser": - "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "fix-lint:browser": "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "fix-lint:main": "tslint --fix --project main/tsconfig.json --config tslint.json", "fix-lint:test": "tslint --fix --project test/tsconfig.json --config tslint.json", "lint": "yarn run lint:browser && yarn run lint:main && yarn run lint:test", - "lint:browser": - "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", + "lint:browser": "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json", "lint:main": "tslint --project main/tsconfig.json --config tslint.json", - "lint:test": - "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", + "lint:test": "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts", "pack": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --publish never", - "ccov:instrument": - "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", - "ccov:test:browser": - "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", - "ccov:remap:browser:html": - "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", - "ccov:remap:browser:lcov": - "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", + "ccov:instrument": "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov", + "ccov:test:browser": "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test", + "ccov:remap:browser:html": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html", + "ccov:remap:browser:lcov": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly", "ccov:clean": "rimraf coverage", "ccov:upload": "codecov", "launch": "electron lib/main/src/main.js", - "start": - "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"", - "start-hot": - "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", + "start": "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"", + "start-hot": "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js", "start-not-dev": "cross-env electron main.js", - "watch:browser": - "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", - "watch:plugins": - "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview", + "watch:browser": "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191", + "watch:plugins": "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview", "watch:plugins:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && tsc --watch", - "watch:plugins:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && tsc --watch", - "install:plugins": - "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier", - "install:plugins:oni-plugin-markdown-preview": - "cd extensions/oni-plugin-markdown-preview && yarn install --prod", - "install:plugins:oni-plugin-prettier": - "cd extensions/oni-plugin-prettier && yarn install --prod", + "watch:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && tsc --watch", + "install:plugins": "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier", + "install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn install --prod", + "install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && yarn install --prod", "postinstall": "yarn run install:plugins && electron-rebuild && opencollective postinstall", - "profile:webpack": - "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" + "profile:webpack": "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json" }, "repository": { "type": "git", @@ -698,7 +860,7 @@ "minimist": "1.2.0", "msgpack-lite": "0.1.26", "ocaml-language-server": "^1.0.27", - "oni-api": "^0.0.42", + "oni-api": "^0.0.44", "oni-neovim-binaries": "0.1.1", "oni-ripgrep": "0.0.4", "oni-types": "0.0.4", diff --git a/test/ci/Api.Buffer.AddLayer.tsx b/test/ci/Api.Buffer.AddLayer.tsx index 5e8541ac90..da342b3b6e 100644 --- a/test/ci/Api.Buffer.AddLayer.tsx +++ b/test/ci/Api.Buffer.AddLayer.tsx @@ -26,7 +26,7 @@ export class TestLayer implements Oni.BufferLayer { className += "inactive" } - return
+ return
{context.visibleLines.join(os.EOL)}
} } @@ -45,13 +45,18 @@ const getInactiveLayerElements = () => { export const test = async (oni: Oni.Plugin.Api) => { await oni.automation.waitForEditors() - await createNewFile("js", oni) + await createNewFile("js", oni, "line1\nline2") oni.editors.activeEditor.activeBuffer.addLayer(new TestLayer()) // Wait for layer to appear await oni.automation.waitFor(() => getLayerElements().length === 1) + // Validate the buffer layer has rendered the 'visibleLines' + const element = getLayerElements()[0] + assert.ok(element.textContent.indexOf("line1") >= 0, "Validate line1 is present in the layer") + assert.ok(element.textContent.indexOf("line2") >= 0, "Validate line2 is present in the layer") + // Validate elements assert.strictEqual(getActiveLayerElements().length, 1) assert.strictEqual(getInactiveLayerElements().length, 0) diff --git a/yarn.lock b/yarn.lock index c75d5ee8dc..b58bffbe13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7322,9 +7322,9 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -oni-api@^0.0.42: - version "0.0.42" - resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.42.tgz#79cab4289809bda1c7b86590119f0c7aaa5a053e" +oni-api@^0.0.44: + version "0.0.44" + resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.44.tgz#26e4999ba5da0be0e7f25b6705c2d5412fe4eb23" oni-neovim-binaries@0.1.1: version "0.1.1" From 46f5707c816e098fbb6aed6978ef51354bcc8032 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Wed, 16 May 2018 17:33:10 -0700 Subject: [PATCH 047/102] Refactoring: Move 'asObservable' from oni-types to oni core (#2215) * Move from oni-types asObservable method to just an asObservable method in core * Fix lint issues * Use updated oni-types version --- .../src/Editor/NeovimEditor/NeovimEditor.tsx | 20 +++++++++---------- browser/src/Editor/NeovimEditor/Symbols.ts | 3 ++- browser/src/Utility.ts | 10 +++++++++- package.json | 2 +- yarn.lock | 6 +++--- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index d4f3a22415..3e2fed29ec 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -75,7 +75,7 @@ import NeovimSurface from "./NeovimSurface" import { ContextMenuManager } from "./../../Services/ContextMenu" -import { normalizePath, sleep } from "./../../Utility" +import { asObservable, normalizePath, sleep } from "./../../Utility" import * as VimConfigurationSynchronizer from "./../../Services/VimConfigurationSynchronizer" @@ -575,25 +575,25 @@ export class NeovimEditor extends Editor implements IEditor { }) // TODO: Does any disposal need to happen for the observables? - this._cursorMoved$ = this._neovimInstance.autoCommands.onCursorMoved - .asObservable() - .map((evt): Oni.Cursor => ({ + this._cursorMoved$ = asObservable(this._neovimInstance.autoCommands.onCursorMoved).map( + (evt): Oni.Cursor => ({ line: evt.line - 1, column: evt.column - 1, - })) + }), + ) - this._cursorMovedI$ = this._neovimInstance.autoCommands.onCursorMovedI - .asObservable() - .map((evt): Oni.Cursor => ({ + this._cursorMovedI$ = asObservable(this._neovimInstance.autoCommands.onCursorMovedI).map( + (evt): Oni.Cursor => ({ line: evt.line - 1, column: evt.column - 1, - })) + }), + ) Observable.merge(this._cursorMoved$, this._cursorMovedI$).subscribe(cursorMoved => { this.notifyCursorMoved(cursorMoved) }) - this._modeChanged$ = this._neovimInstance.onModeChanged.asObservable() + this._modeChanged$ = asObservable(this._neovimInstance.onModeChanged) this.trackDisposable( this._neovimInstance.onModeChanged.subscribe(newMode => this._onModeChanged(newMode)), diff --git a/browser/src/Editor/NeovimEditor/Symbols.ts b/browser/src/Editor/NeovimEditor/Symbols.ts index 921c7f0740..598a26c384 100644 --- a/browser/src/Editor/NeovimEditor/Symbols.ts +++ b/browser/src/Editor/NeovimEditor/Symbols.ts @@ -13,6 +13,7 @@ import { LanguageManager } from "./../../Services/Language" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" +import { asObservable } from "./../../Utility" import { Definition } from "./Definition" export class Symbols { @@ -34,7 +35,7 @@ export class Symbols { ]) menu.setLoading(true) - const filterTextChanged$ = menu.onFilterTextChanged.asObservable() + const filterTextChanged$ = asObservable(menu.onFilterTextChanged) menu.onItemSelected.subscribe((selectedItem: Oni.Menu.MenuOption) => { const key = selectedItem.label + selectedItem.detail diff --git a/browser/src/Utility.ts b/browser/src/Utility.ts index 92a177083f..9a6ba0db62 100644 --- a/browser/src/Utility.ts +++ b/browser/src/Utility.ts @@ -16,7 +16,7 @@ import { Observable } from "rxjs/Observable" import { Subject } from "rxjs/Subject" import * as JSON5 from "json5" -import { IDisposable } from "oni-types" +import { IDisposable, IEvent } from "oni-types" import * as types from "vscode-languageserver-types" @@ -41,6 +41,14 @@ export class Disposable implements IDisposable { } } +export const asObservable = (event: IEvent): Observable => { + const subject = new Subject() + + event.subscribe((val: T) => subject.next(val)) + + return subject +} + /** * Use a `node` require instead of a `webpack` require * The difference is that `webpack` require will bake the javascript diff --git a/package.json b/package.json index c32d51307f..b27b233816 100644 --- a/package.json +++ b/package.json @@ -863,7 +863,7 @@ "oni-api": "^0.0.44", "oni-neovim-binaries": "0.1.1", "oni-ripgrep": "0.0.4", - "oni-types": "0.0.4", + "oni-types": "^0.0.8", "react": "16.0.0", "react-dnd": "^2.5.4", "react-dnd-html5-backend": "^2.5.4", diff --git a/yarn.lock b/yarn.lock index b58bffbe13..fcc25a6565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7344,9 +7344,9 @@ oni-ripgrep@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/oni-ripgrep/-/oni-ripgrep-0.0.4.tgz#8eb52383f4a3f92b8b5d8fe29d52e9d728a46b50" -oni-types@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/oni-types/-/oni-types-0.0.4.tgz#97bc435565d5f4c3bdc50bd1700fd24dd5b6704b" +oni-types@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/oni-types/-/oni-types-0.0.8.tgz#1dfe97eda3f2b97dfcb94609af25fc2e4172fa33" oniguruma@^6.0.1: version "6.2.1" From 291f49916b5afb370b7c37de2262823e61e97900 Mon Sep 17 00:00:00 2001 From: keforbes Date: Thu, 17 May 2018 15:20:43 -0600 Subject: [PATCH 048/102] fix implementation --- .../Services/Sidebar/SidebarContentSplit.tsx | 8 ++---- browser/src/Services/Sidebar/SidebarStore.ts | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/browser/src/Services/Sidebar/SidebarContentSplit.tsx b/browser/src/Services/Sidebar/SidebarContentSplit.tsx index 9831cfd508..0171097e79 100644 --- a/browser/src/Services/Sidebar/SidebarContentSplit.tsx +++ b/browser/src/Services/Sidebar/SidebarContentSplit.tsx @@ -8,7 +8,6 @@ import styled, { keyframes } from "styled-components" import { Event, IDisposable, IEvent } from "oni-types" -import { configuration } from "../Configuration" import { enableMouse, withProps } from "./../../UI/components/common" import { ISidebarEntry, ISidebarState, SidebarManager, SidebarPane } from "./SidebarStore" @@ -65,6 +64,7 @@ export class SidebarContentSplit { export interface ISidebarContentViewProps extends ISidebarContentContainerProps { activeEntry: ISidebarEntry + width: string } export interface ISidebarContentContainerProps { @@ -174,10 +174,7 @@ export class SidebarContentView extends React.PureComponent< const header = activeEntry && activeEntry.pane ? activeEntry.pane.title : null return ( - + {activeEntry.pane.render()} @@ -200,6 +197,7 @@ export const mapStateToProps = ( return { ...containerProps, activeEntry, + width: state.width, } } diff --git a/browser/src/Services/Sidebar/SidebarStore.ts b/browser/src/Services/Sidebar/SidebarStore.ts index 61029044e9..4c08a09a05 100644 --- a/browser/src/Services/Sidebar/SidebarStore.ts +++ b/browser/src/Services/Sidebar/SidebarStore.ts @@ -7,6 +7,7 @@ import { Reducer, Store } from "redux" import { createStore as createReduxStore } from "./../../Redux" +import { configuration } from "../Configuration" import { WindowManager, WindowSplitHandle } from "./../WindowManager" import { SidebarContentSplit } from "./SidebarContentSplit" import { SidebarSplit } from "./SidebarSplit" @@ -20,6 +21,8 @@ export interface ISidebarState { activeEntryId: string isActive: boolean + + width: string } export type SidebarIcon = string @@ -65,6 +68,12 @@ export class SidebarManager { constructor(private _windowManager: WindowManager = null) { this._store = createStore() + configuration.onConfigurationChanged.subscribe(val => { + if (typeof val["sidebar.width"] === "string") { + this.setWidth(val["sidebar.width"]) + } + }) + if (_windowManager) { this._iconSplit = this._windowManager.createSplit("left", new SidebarSplit(this)) this._contentSplit = this._windowManager.createSplit( @@ -74,6 +83,15 @@ export class SidebarManager { } } + public setWidth(width: string): void { + if (width) { + this._store.dispatch({ + type: "SET_WIDTH", + width: width, + }) + } + } + public setNotification(id: string): void { if (id) { this._store.dispatch({ @@ -139,6 +157,7 @@ const DefaultSidebarState: ISidebarState = { entries: [], activeEntryId: null, isActive: false, + width: configuration.getValue("sidebar.width"), } export type SidebarActions = @@ -154,6 +173,10 @@ export type SidebarActions = type: "SET_NOTIFICATION" id: string } + | { + type: "SET_WIDTH" + width: string + } | { type: "ENTER" } @@ -181,6 +204,11 @@ export const sidebarReducer: Reducer = ( ...newState, isActive: false, } + case "SET_WIDTH": + return { + ...newState, + width: action.width, + } case "SET_ACTIVE_ID": return { ...newState, From fddbb84ff11e2ca677abcc96360bfbd9cce32485 Mon Sep 17 00:00:00 2001 From: keforbes Date: Thu, 17 May 2018 15:22:36 -0600 Subject: [PATCH 049/102] fix lint issue --- browser/src/Services/Sidebar/SidebarStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/src/Services/Sidebar/SidebarStore.ts b/browser/src/Services/Sidebar/SidebarStore.ts index 4c08a09a05..d19e375b20 100644 --- a/browser/src/Services/Sidebar/SidebarStore.ts +++ b/browser/src/Services/Sidebar/SidebarStore.ts @@ -87,7 +87,7 @@ export class SidebarManager { if (width) { this._store.dispatch({ type: "SET_WIDTH", - width: width, + width, }) } } From def017083e2fbc69621570a67d4530cb1d26e697 Mon Sep 17 00:00:00 2001 From: keforbes Date: Thu, 17 May 2018 15:46:16 -0600 Subject: [PATCH 050/102] set sidebar width from config during initial Oni launch --- browser/src/Services/Sidebar/SidebarStore.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/Sidebar/SidebarStore.ts b/browser/src/Services/Sidebar/SidebarStore.ts index d19e375b20..43931a8e95 100644 --- a/browser/src/Services/Sidebar/SidebarStore.ts +++ b/browser/src/Services/Sidebar/SidebarStore.ts @@ -73,6 +73,7 @@ export class SidebarManager { this.setWidth(val["sidebar.width"]) } }) + this.setWidth(configuration.getValue("sidebar.width")) if (_windowManager) { this._iconSplit = this._windowManager.createSplit("left", new SidebarSplit(this)) @@ -157,7 +158,7 @@ const DefaultSidebarState: ISidebarState = { entries: [], activeEntryId: null, isActive: false, - width: configuration.getValue("sidebar.width"), + width: null, } export type SidebarActions = From ecc6c4a5482f691633fee04fbd1562de12f5652f Mon Sep 17 00:00:00 2001 From: Kazuyuki Kamata Date: Fri, 18 May 2018 11:52:17 +0900 Subject: [PATCH 051/102] Bugfix: oni.input.unbind() (#2216) * Bugfix: oni.input.unbind() * Add a unit test for unbinding an array --- browser/src/Services/InputManager.ts | 2 +- browser/test/Input/InputManagerTests.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/InputManager.ts b/browser/src/Services/InputManager.ts index 89daa64858..beedd8da39 100644 --- a/browser/src/Services/InputManager.ts +++ b/browser/src/Services/InputManager.ts @@ -103,7 +103,7 @@ export class InputManager implements Oni.Input.InputManager { public unbind(keyChord: string | string[]) { if (Array.isArray(keyChord)) { - keyChord.forEach(key => this.unbind(keyChord)) + keyChord.forEach(key => this.unbind(key)) return } diff --git a/browser/test/Input/InputManagerTests.ts b/browser/test/Input/InputManagerTests.ts index d478655957..5591b55a81 100644 --- a/browser/test/Input/InputManagerTests.ts +++ b/browser/test/Input/InputManagerTests.ts @@ -53,6 +53,15 @@ describe("InputManager", () => { assert.strictEqual(handled, false) }) + it("can unbind an array of keys", () => { + const im = new InputManager() + im.bind(["a", "b"], "test.command") + im.unbind(["a", "b"]) + + const boundKeys = im.getBoundKeys("test.command") + assert.deepEqual(boundKeys, [], "Validate no bound keys are returned") + }) + describe("getBoundKeys", () => { it("returns empty array if no key bound to command", () => { const im = new InputManager() From 7740d10289ca112cc55c2305317d32178e0405d6 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sat, 19 May 2018 15:36:48 +0200 Subject: [PATCH 052/102] Ensure that the `offsetGlyphVariantCount` is an integer --- browser/src/Renderer/WebGL/WebGLRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/src/Renderer/WebGL/WebGLRenderer.ts b/browser/src/Renderer/WebGL/WebGLRenderer.ts index 6a176b8ae3..3f585f686c 100644 --- a/browser/src/Renderer/WebGL/WebGLRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLRenderer.ts @@ -76,7 +76,7 @@ export class WebGLRenderer implements INeovimRenderer { fontSize, }: IScreen) { const devicePixelRatio = window.devicePixelRatio - const offsetGlyphVariantCount = Math.max(4 / devicePixelRatio, 1) + const offsetGlyphVariantCount = Math.max(Math.ceil(4 / devicePixelRatio), 1) const atlasOptions = { fontFamily, fontSize, From c7ae47871708a5d45aa299c123ab676e0669377e Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Sat, 19 May 2018 17:07:36 +0200 Subject: [PATCH 053/102] Add missing import for the RxJS `debounceTime` operator --- browser/src/Services/Configuration/FileConfigurationProvider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/src/Services/Configuration/FileConfigurationProvider.ts b/browser/src/Services/Configuration/FileConfigurationProvider.ts index d945c175a6..912d8803ed 100644 --- a/browser/src/Services/Configuration/FileConfigurationProvider.ts +++ b/browser/src/Services/Configuration/FileConfigurationProvider.ts @@ -9,6 +9,7 @@ import * as isError from "lodash/isError" import * as mkdirp from "mkdirp" import * as path from "path" +import "rxjs/add/operator/debounceTime" import { Subject } from "rxjs/Subject" import * as Oni from "oni-api" From 4b90495a3272e4c5151b3593696d75ff69377623 Mon Sep 17 00:00:00 2001 From: Manuel Hornung Date: Tue, 22 May 2018 12:46:16 +0200 Subject: [PATCH 054/102] WebGLRenderer: Lazily (re)initialize the glyph buffer with sufficient size (#2224) * Lazily (re)initialize the glyph buffer in the WebGLRenderer to ensure sufficient length for a completely filled screen * Apply the same fix to the solid cell renderer --- browser/src/Renderer/WebGL/WebGLSolidRenderer.ts | 12 +++++++++--- browser/src/Renderer/WebGL/WebGLTextRenderer.ts | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/browser/src/Renderer/WebGL/WebGLSolidRenderer.ts b/browser/src/Renderer/WebGL/WebGLSolidRenderer.ts index dadafec5ed..51abf7aa73 100644 --- a/browser/src/Renderer/WebGL/WebGLSolidRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLSolidRenderer.ts @@ -6,8 +6,6 @@ import { createUnitQuadVerticesBuffer, } from "./WebGLUtilities" -// tslint:disable-next-line:no-bitwise -const maxCellInstances = 1 << 14 // TODO find a reasonable way of determining this const solidInstanceFieldCount = 8 const solidInstanceSizeInBytes = solidInstanceFieldCount * Float32Array.BYTES_PER_ELEMENT @@ -81,6 +79,8 @@ export class WebGLSolidRenderer { viewportScaleX: number, viewportScaleY: number, ) { + const cellCount = columnCount * rowCount + this.recreateSolidInstancesArrayIfRequired(cellCount) const solidInstanceCount = this.populateSolidInstances( columnCount, rowCount, @@ -95,7 +95,6 @@ export class WebGLSolidRenderer { private createBuffers() { this._unitQuadVerticesBuffer = createUnitQuadVerticesBuffer(this._gl) this._unitQuadElementIndicesBuffer = createUnitQuadElementIndicesBuffer(this._gl) - this._solidInstances = new Float32Array(maxCellInstances * solidInstanceFieldCount) this._solidInstancesBuffer = this._gl.createBuffer() } @@ -152,6 +151,13 @@ export class WebGLSolidRenderer { this._gl.vertexAttribDivisor(vertexShaderAttributes.colorRGBA, 1) } + private recreateSolidInstancesArrayIfRequired(cellCount: number) { + const requiredArrayLength = cellCount * solidInstanceFieldCount + if (!this._solidInstances || this._solidInstances.length < requiredArrayLength) { + this._solidInstances = new Float32Array(requiredArrayLength) + } + } + private populateSolidInstances( columnCount: number, rowCount: number, diff --git a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts index 31b8808e47..c8348fec4e 100644 --- a/browser/src/Renderer/WebGL/WebGLTextRenderer.ts +++ b/browser/src/Renderer/WebGL/WebGLTextRenderer.ts @@ -7,8 +7,6 @@ import { createUnitQuadVerticesBuffer, } from "./WebGLUtilities" -// tslint:disable-next-line:no-bitwise -const maxGlyphInstances = 1 << 14 // TODO find a reasonable way of determining this const glyphInstanceFieldCount = 13 const glyphInstanceSizeInBytes = glyphInstanceFieldCount * Float32Array.BYTES_PER_ELEMENT @@ -162,6 +160,8 @@ export class WebGlTextRenderer { viewportScaleX: number, viewportScaleY: number, ) { + const cellCount = columnCount * rowCount + this.recreateGlyphInstancesArrayIfRequired(cellCount) const glyphInstanceCount = this.populateGlyphInstances( columnCount, rowCount, @@ -176,7 +176,6 @@ export class WebGlTextRenderer { private createBuffers() { this._unitQuadVerticesBuffer = createUnitQuadVerticesBuffer(this._gl) this._unitQuadElementIndicesBuffer = createUnitQuadElementIndicesBuffer(this._gl) - this._glyphInstances = new Float32Array(maxGlyphInstances * glyphInstanceFieldCount) this._glyphInstancesBuffer = this._gl.createBuffer() } @@ -266,6 +265,13 @@ export class WebGlTextRenderer { this._gl.vertexAttribDivisor(vertexShaderAttributes.atlasSize, 1) } + private recreateGlyphInstancesArrayIfRequired(cellCount: number) { + const requiredArrayLength = cellCount * glyphInstanceFieldCount + if (!this._glyphInstances || this._glyphInstances.length < requiredArrayLength) { + this._glyphInstances = new Float32Array(requiredArrayLength) + } + } + private populateGlyphInstances( columnCount: number, rowCount: number, From f507d7235a9a489817891b96922df2b4cb8ad97f Mon Sep 17 00:00:00 2001 From: satotake Date: Wed, 23 May 2018 11:00:55 +0900 Subject: [PATCH 055/102] Config: add "editor.fontWeight" (#2232) --- browser/src/Editor/NeovimEditor/NeovimEditor.tsx | 5 +++-- .../src/Editor/NeovimEditor/NeovimEditorActions.ts | 4 +++- .../src/Editor/NeovimEditor/NeovimEditorReducer.ts | 1 + .../src/Editor/NeovimEditor/NeovimEditorStore.ts | 2 ++ browser/src/Font.ts | 8 +++++++- browser/src/Renderer/CanvasRenderer.ts | 5 ++++- .../Services/Configuration/DefaultConfiguration.ts | 1 + .../Services/Configuration/IConfigurationValues.ts | 1 + browser/src/neovim/NeovimInstance.ts | 14 ++++++++++++-- browser/src/neovim/Screen.ts | 7 +++++++ browser/src/neovim/actions.ts | 4 ++++ browser/test/Mocks/neovim.ts | 3 +++ 12 files changed, 48 insertions(+), 7 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index 3e2fed29ec..b702148fb0 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -1205,10 +1205,11 @@ export class NeovimEditor extends Editor implements IEditor { private _onConfigChanged(newValues: Partial): void { const fontFamily = this._configuration.getValue("editor.fontFamily") const fontSize = addDefaultUnitIfNeeded(this._configuration.getValue("editor.fontSize")) + const fontWeight = this._configuration.getValue("editor.fontWeight") const linePadding = this._configuration.getValue("editor.linePadding") - this._actions.setFont(fontFamily, fontSize) - this._neovimInstance.setFont(fontFamily, fontSize, linePadding) + this._actions.setFont(fontFamily, fontSize, fontWeight) + this._neovimInstance.setFont(fontFamily, fontSize, fontWeight, linePadding) Object.keys(newValues).forEach(key => { const value = newValues[key] diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts b/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts index fc8f7b3849..df68087414 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorActions.ts @@ -142,6 +142,7 @@ export interface ISetFont { payload: { fontFamily: string fontSize: string + fontWeight: string } } @@ -493,11 +494,12 @@ export const setImeActive = (imeActive: boolean) => ({ }, }) -export const setFont = (fontFamily: string, fontSize: string) => ({ +export const setFont = (fontFamily: string, fontSize: string, fontWeight: string) => ({ type: "SET_FONT", payload: { fontFamily, fontSize, + fontWeight, }, }) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts b/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts index 81b9e2ea1a..3b91a43f21 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorReducer.ts @@ -77,6 +77,7 @@ export function reducer( ...s, fontFamily: a.payload.fontFamily, fontSize: a.payload.fontSize, + fontWeight: a.payload.fontWeight, } case "SET_MODE": return { ...s, ...{ mode: a.payload.mode } } diff --git a/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts b/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts index 3b57ef2664..6ec4827068 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts +++ b/browser/src/Editor/NeovimEditor/NeovimEditorStore.ts @@ -58,6 +58,7 @@ export interface IState { fontPixelHeight: number fontFamily: string fontSize: string + fontWeight: string hasFocus: boolean mode: string definition: null | IDefinition @@ -189,6 +190,7 @@ export const createDefaultState = (): IState => ({ fontPixelHeight: 10, fontFamily: "", fontSize: "", + fontWeight: "", hasFocus: false, imeActive: false, mode: "normal", diff --git a/browser/src/Font.ts b/browser/src/Font.ts index 4336a87433..331745d293 100644 --- a/browser/src/Font.ts +++ b/browser/src/Font.ts @@ -6,7 +6,12 @@ export interface IFontMeasurement { height: number } -export function measureFont(fontFamily: string, fontSize: string, characterToTest = "H") { +export function measureFont( + fontFamily: string, + fontSize: string, + fontWeight: string, + characterToTest = "H", +) { const div = document.createElement("div") div.style.position = "absolute" @@ -18,6 +23,7 @@ export function measureFont(fontFamily: string, fontSize: string, characterToTes div.textContent = characterToTest div.style.fontFamily = `${fontFamily},${FallbackFonts}` div.style.fontSize = fontSize + div.style.fontWeight = fontWeight const isItalicAvailable = isStyleAvailable(fontFamily, "italic", fontSize) const isBoldAvailable = isStyleAvailable(fontFamily, "bold", fontSize) diff --git a/browser/src/Renderer/CanvasRenderer.ts b/browser/src/Renderer/CanvasRenderer.ts index 0b7d3eb653..4504b8672c 100644 --- a/browser/src/Renderer/CanvasRenderer.ts +++ b/browser/src/Renderer/CanvasRenderer.ts @@ -123,13 +123,16 @@ export class CanvasRenderer implements INeovimRenderer { public _draw(screenInfo: IScreen, modifiedCells: IPosition[]): void { Performance.mark("CanvasRenderer.update.start") - this._canvasContext.font = screenInfo.fontSize + " " + screenInfo.fontFamily + this._canvasContext.font = `${screenInfo.fontWeight} ${screenInfo.fontSize} ${ + screenInfo.fontFamily + }` this._canvasContext.textBaseline = "top" this._canvasContext.setTransform(this._devicePixelRatio, 0, 0, this._devicePixelRatio, 0, 0) this._canvasContext.imageSmoothingEnabled = false this._editorElement.style.fontFamily = screenInfo.fontFamily this._editorElement.style.fontSize = screenInfo.fontSize + this._editorElement.style.fontWeight = screenInfo.fontWeight const rowsToEdit = getSpansToEdit(this._grid, modifiedCells) diff --git a/browser/src/Services/Configuration/DefaultConfiguration.ts b/browser/src/Services/Configuration/DefaultConfiguration.ts index a631e8c4a9..79bdae8a29 100644 --- a/browser/src/Services/Configuration/DefaultConfiguration.ts +++ b/browser/src/Services/Configuration/DefaultConfiguration.ts @@ -104,6 +104,7 @@ const BaseConfiguration: IConfigurationValues = { "editor.fontLigatures": true, "editor.fontSize": "12px", + "editor.fontWeight": "normal", "editor.fontFamily": "", "editor.linePadding": 2, diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index 7ee137cf63..60eb8ad501 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -153,6 +153,7 @@ export interface IConfigurationValues { // If true (default), ligatures are enabled "editor.fontLigatures": boolean "editor.fontSize": string + "editor.fontWeight": string "editor.fontFamily": string // Platform specific // Additional padding between lines diff --git a/browser/src/neovim/NeovimInstance.ts b/browser/src/neovim/NeovimInstance.ts index f2ecc0eb74..9df1a3c282 100644 --- a/browser/src/neovim/NeovimInstance.ts +++ b/browser/src/neovim/NeovimInstance.ts @@ -182,7 +182,7 @@ export interface INeovimInstance { // - Refactor remaining events into strongly typed events, as part of the interface on(event: string, handler: NeovimEventHandler): void - setFont(fontFamily: string, fontSize: string, linePadding: number): void + setFont(fontFamily: string, fontSize: string, fontWeight: string, linePadding: number): void getBufferIds(): Promise @@ -209,6 +209,7 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { private _fontFamily: string private _fontSize: string + private _fontWeight: string private _fontWidthInPixels: number private _fontHeightInPixels: number @@ -369,6 +370,7 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { this._configuration = configuration this._fontFamily = this._configuration.getValue("editor.fontFamily") this._fontSize = addDefaultUnitIfNeeded(this._configuration.getValue("editor.fontSize")) + this._fontWeight = this._configuration.getValue("editor.fontWeight") this._lastWidthInPixels = widthInPixels this._lastHeightInPixels = heightInPixels @@ -518,13 +520,20 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { return ret } - public setFont(fontFamily: string, fontSize: string, linePadding: number): void { + public setFont( + fontFamily: string, + fontSize: string, + fontWeight: string, + linePadding: number, + ): void { this._fontFamily = fontFamily this._fontSize = fontSize + this._fontWeight = fontWeight const { width, height, isBoldAvailable, isItalicAvailable } = measureFont( this._fontFamily, this._fontSize, + this._fontWeight, ) this._fontWidthInPixels = width @@ -535,6 +544,7 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { Actions.setFont({ fontFamily, fontSize, + fontWeight, fontWidthInPixels: width, fontHeightInPixels: height + linePadding, linePaddingInPixels: linePadding, diff --git a/browser/src/neovim/Screen.ts b/browser/src/neovim/Screen.ts index 7fe7642741..57f98e4939 100644 --- a/browser/src/neovim/Screen.ts +++ b/browser/src/neovim/Screen.ts @@ -27,6 +27,7 @@ export interface IScreen { fontFamily: null | string fontHeightInPixels: number fontSize: null | string + fontWeight: null | string fontWidthInPixels: number foregroundColor: string height: number @@ -84,6 +85,7 @@ export class NeovimScreen implements IScreen { private _fontFamily: null | string = null private _fontHeightInPixels: number private _fontSize: null | string = null + private _fontWeight: null | string = null private _fontWidthInPixels: number private _foregroundColor: string = "#000000" private _grid: Grid = new Grid() @@ -109,6 +111,10 @@ export class NeovimScreen implements IScreen { return this._fontSize } + public get fontWeight(): null | string { + return this._fontWeight + } + public get fontWidthInPixels(): number { return this._fontWidthInPixels } @@ -249,6 +255,7 @@ export class NeovimScreen implements IScreen { case Actions.SET_FONT: this._fontFamily = action.fontFamily this._fontSize = action.fontSize + this._fontWeight = action.fontWeight this._fontWidthInPixels = action.fontWidthInPixels this._fontHeightInPixels = action.fontHeightInPixels this._linePaddingInPixels = action.linePaddingInPixels diff --git a/browser/src/neovim/actions.ts b/browser/src/neovim/actions.ts index 9cb4811d69..8560f6513c 100644 --- a/browser/src/neovim/actions.ts +++ b/browser/src/neovim/actions.ts @@ -42,6 +42,7 @@ export interface IKeyboardInputAction extends IAction { interface ISetFontArguments { fontFamily: string fontSize: string + fontWeight: string fontWidthInPixels: number fontHeightInPixels: number linePaddingInPixels: number @@ -52,6 +53,7 @@ interface ISetFontArguments { export interface ISetFontAction extends IAction { fontFamily: string fontSize: string + fontWeight: string fontWidthInPixels: number fontHeightInPixels: number linePaddingInPixels: number @@ -204,6 +206,7 @@ export function changeMode(mode: string): IChangeModeAction { export function setFont({ fontFamily, fontSize, + fontWeight, fontWidthInPixels, fontHeightInPixels, linePaddingInPixels, @@ -214,6 +217,7 @@ export function setFont({ type: SET_FONT, fontFamily, fontSize, + fontWeight, fontWidthInPixels, fontHeightInPixels, linePaddingInPixels, diff --git a/browser/test/Mocks/neovim.ts b/browser/test/Mocks/neovim.ts index 5007bc3948..d06c325252 100644 --- a/browser/test/Mocks/neovim.ts +++ b/browser/test/Mocks/neovim.ts @@ -34,6 +34,9 @@ export class MockScreen implements Neovim.IScreen { public get fontSize(): null | string { return null } + public get fontWeight(): null | string { + return null + } public get fontWidthInPixels(): number { return null } From 730d4120daf42af1ecd9bc4d919693cfbbfef6a5 Mon Sep 17 00:00:00 2001 From: Akin Date: Wed, 23 May 2018 06:27:17 +0100 Subject: [PATCH 056/102] Feature/ upgrade prettier and pretty-quick (#2228) Upgrade prettier and the pretty quick package which runs it as a pre-commit hook the main reason for this being that prettier incrementally improve their parsing and formatting strategy and crucially for a `typescript` project they add support for new ts syntax that prettier would otherwise choke on without upgrades --- package.json | 4 ++-- yarn.lock | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index b27b233816..dd6f214fa4 100644 --- a/package.json +++ b/package.json @@ -958,8 +958,8 @@ "nyc": "^11.4.1", "oni-release-downloader": "^0.0.10", "opencollective": "1.0.3", - "prettier": "^1.10.2", - "pretty-quick": "^1.2.2", + "prettier": "^1.12.1", + "pretty-quick": "^1.5.0", "react-hot-loader": "^4.0.1", "react-motion": "0.5.2", "react-redux": "5.0.6", diff --git a/yarn.lock b/yarn.lock index fcc25a6565..72da42e389 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8008,11 +8008,7 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" - -prettier@^1.5.3: +prettier@^1.12.1, prettier@^1.5.3: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" @@ -8034,9 +8030,9 @@ pretty-format@^22.1.0: ansi-regex "^3.0.0" ansi-styles "^3.2.0" -pretty-quick@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-1.2.2.tgz#4ebb2bafa86aa5f67948c3d2ead6f196770f9e32" +pretty-quick@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-1.5.0.tgz#304853ece7f8cb56bec74ba3ccd978037e7f2117" dependencies: chalk "^2.3.0" execa "^0.8.0" From 47e94d94454b28d04d5abd54d844d455b0f57e39 Mon Sep 17 00:00:00 2001 From: Akin Date: Wed, 23 May 2018 06:28:11 +0100 Subject: [PATCH 057/102] Feature/upgrade react to 16.3 (#2229) * Add missing import for the RxJS `debounceTime` operator * upgrade to react 16.3 --- package.json | 4 ++-- yarn.lock | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index dd6f214fa4..19c9d38c82 100644 --- a/package.json +++ b/package.json @@ -864,10 +864,10 @@ "oni-neovim-binaries": "0.1.1", "oni-ripgrep": "0.0.4", "oni-types": "^0.0.8", - "react": "16.0.0", + "react": "^16.3.2", "react-dnd": "^2.5.4", "react-dnd-html5-backend": "^2.5.4", - "react-dom": "16.0.0", + "react-dom": "^16.3.2", "redux-batched-subscribe": "^0.1.6", "shell-env": "^0.3.0", "shelljs": "0.7.7", diff --git a/yarn.lock b/yarn.lock index 72da42e389..2016b35a21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8275,9 +8275,9 @@ react-dnd@^2.5.4: lodash "^4.2.0" prop-types "^15.5.10" -react-dom@16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.0.0.tgz#9cc3079c3dcd70d4c6e01b84aab2a7e34c303f58" +react-dom@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -8360,9 +8360,9 @@ react-virtualized@^9.18.0: loose-envify "^1.3.0" prop-types "^15.5.4" -react@16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.0.0.tgz#ce7df8f1941b036f02b2cca9dbd0cb1f0e855e2d" +react@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" From 2e79e328b20e1fa122de041829ddc59790cef2dc Mon Sep 17 00:00:00 2001 From: Akin Date: Wed, 23 May 2018 06:29:21 +0100 Subject: [PATCH 058/102] fix parsing issues in ui-test tabs (#2226) --- ui-tests/Tabs.test.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ui-tests/Tabs.test.tsx b/ui-tests/Tabs.test.tsx index bbb3261cfb..ae55c6f6ea 100644 --- a/ui-tests/Tabs.test.tsx +++ b/ui-tests/Tabs.test.tsx @@ -9,9 +9,12 @@ import { Sneakable } from "../browser/src/UI/components/Sneakable" import { Tab, Tabs } from "../browser/src/UI/components/Tabs" +const MockFileIcon = FileIcon as jest.Mock<{}> +const MockSneakable = Sneakable as jest.Mock + describe(" Tests", () => { - FileIcon.mockImplementation(() => ({ render: jest.fn() })) - Sneakable.mockImplementation((props) => ({ + MockFileIcon.mockImplementation(() => ({ render: jest.fn() })) + MockSneakable.mockImplementation(props => ({ render: () => props.children, })) @@ -177,12 +180,12 @@ describe(" Tests", () => { expect(sneakable.props().tag).toEqual(tab.props().name) wrapper.unmount() - }); + }) it("should call onTabSelect callback as Sneakable callback", () => { - Sneakable.mockImplementationOnce((props) => { + MockSneakable.mockImplementationOnce(props => { props.callback() - } + }) const wrapper = mount(TabsContainingSingleTab) const sneakableTab = wrapper.find(Tab) @@ -190,7 +193,7 @@ describe(" Tests", () => { expect(tabSelectFunction).toHaveBeenCalledWith(sneakableTab.props().id) wrapper.unmount() - }); + }) it("should pass tab icon file name as FileIcon fileName property ", () => { const wrapper = mount(TabsContainingSingleTab) @@ -200,5 +203,5 @@ describe(" Tests", () => { expect(fileIcon.props().fileName).toEqual(tab.props().iconFileName) wrapper.unmount() - }); + }) }) From 38566d92489ebce97d82a899a1fda8467eff786c Mon Sep 17 00:00:00 2001 From: Akin Date: Wed, 23 May 2018 06:30:14 +0100 Subject: [PATCH 059/102] remove call to clear the oni-backup dir on oni startup (#2227) --- browser/src/Services/Explorer/ExplorerFileSystem.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/browser/src/Services/Explorer/ExplorerFileSystem.ts b/browser/src/Services/Explorer/ExplorerFileSystem.ts index 7ad60071e7..9f716fa0fe 100644 --- a/browser/src/Services/Explorer/ExplorerFileSystem.ts +++ b/browser/src/Services/Explorer/ExplorerFileSystem.ts @@ -5,7 +5,7 @@ */ import * as fs from "fs" -import { emptyDirSync, ensureDirSync, mkdirp, move, pathExists, remove, writeFile } from "fs-extra" +import { ensureDirSync, mkdirp, move, pathExists, remove, writeFile } from "fs-extra" import * as os from "os" import * as path from "path" import { promisify } from "util" @@ -53,7 +53,6 @@ export class FileSystem implements IFileSystem { public init = () => { ensureDirSync(this._backupDirectory) - emptyDirSync(this._backupDirectory) } public async readdir(directoryPath: string): Promise { From 0b21bfce5125f3fe9b87a00e9b4dabeced82afca Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 25 May 2018 18:47:11 -0700 Subject: [PATCH 060/102] Add Bryan Germann as a backer - thank you! :) --- BACKERS.md | 119 +++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/BACKERS.md b/BACKERS.md index 8cf5fd5c50..1f079a28c8 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -4,11 +4,11 @@ Oni is an MIT-licensed open-source project. It's an independent project without If you use Oni, please consider joining them via the following options: -* Become a backer on [Patreon](https://patreon.com/onivim) -* Become a backer on [OpenCollective](https://opencollective.com/oni#backer) -* Become a backer on [Bountysource](https://salt.bountysource.com/teams/oni) -* Make a donation via [PayPal](https://www.paypal.me/bryphe/25) -* Make a donation via Bitcoin / Ethereum (coming soon) +* Become a backer on [Patreon](https://patreon.com/onivim) +* Become a backer on [OpenCollective](https://opencollective.com/oni#backer) +* Become a backer on [Bountysource](https://salt.bountysource.com/teams/oni) +* Make a donation via [PayPal](https://www.paypal.me/bryphe/25) +* Make a donation via Bitcoin / Ethereum (coming soon) Thanks you to all our backers for making Oni possible! @@ -55,75 +55,76 @@ Thanks you to all our backers for making Oni possible! ## VIP Backers via BountySource -* @jordwalke -* @mhartington -* @MikaAK -* @emolitor +* @jordwalke +* @mhartington +* @MikaAK +* @emolitor ## VIP Backers via Patreon -* @mikl -* Tom Boland +* @mikl +* Tom Boland ## Backers via BountySource -* @adambard -* @akin_so -* @ayohan -* @badosu -* @josemarluedke -* @napcode -* @robtrac -* @rrichardson -* @sbuljac -* @parkerault -* @city41 -* @nithesh -* @erandac -* @appelgriebsch +* @adambard +* @akin_so +* @ayohan +* @badosu +* @josemarluedke +* @napcode +* @robtrac +* @rrichardson +* @sbuljac +* @parkerault +* @city41 +* @nithesh +* @erandac +* @appelgriebsch ## Backers via PayPal -* @mchalkley -* @am2605 -* Nathan Ensmenger +* @mchalkley +* @am2605 +* Nathan Ensmenger ## Backers via OpenCollective -* Tal Amuyal -* Akinola Sowemimo -* Martijn Arts -* Amadeus Folego -* Kiyoshi Murata -* @Himura2la +* Tal Amuyal +* Akinola Sowemimo +* Martijn Arts +* Amadeus Folego +* Kiyoshi Murata +* @Himura2la ## Backers via Patreon -* @bennettrogers -* @muream -* Johnnie HÃ¥rd -* @robin-pham -* Ryan Campbell -* Balint Fulop -* Quasar Jarosz -* Channing Conger -* Clinton Bloodworth -* Lex Song -* Paul Baumgart -* Kaiden Sin -* Troy Vitullo -* Leo Critchley -* Patrick Massot -* Jerome Pellois -* Wesley Moore -* Kim Fiedler -* Nicolaus Hepler -* Nick Price -* Domenico Maisano -* Daniel Polanco -* Eric Hall -* Dimas Cyriaco -* Carlos Coves Prieto +* @bennettrogers +* @muream +* Johnnie HÃ¥rd +* @robin-pham +* Ryan Campbell +* Balint Fulop +* Quasar Jarosz +* Channing Conger +* Clinton Bloodworth +* Lex Song +* Paul Baumgart +* Kaiden Sin +* Troy Vitullo +* Leo Critchley +* Patrick Massot +* Jerome Pellois +* Wesley Moore +* Kim Fiedler +* Nicolaus Hepler +* Nick Price +* Domenico Maisano +* Daniel Polanco +* Eric Hall +* Dimas Cyriaco +* Carlos Coves Prieto +* Bryan Germann From 48a681685cab81c215332ddf9d81d2524bb36058 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 25 May 2018 18:47:42 -0700 Subject: [PATCH 061/102] Add James Herdman as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 1f079a28c8..6535b508e4 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -125,6 +125,7 @@ Thanks you to all our backers for making Oni possible! * Dimas Cyriaco * Carlos Coves Prieto * Bryan Germann +* James Herdman From b5d452d792c6f1775cf0c1a6b9cd58b569df2117 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 25 May 2018 20:35:02 -0700 Subject: [PATCH 062/102] Dependency: Upgrade to electron 2.0.1 (#2218) Dependency: Upgrade to electron 2.0.2 --- package.json | 5 +- test/common/runInProcTest.ts | 12 +++-- yarn.lock | 90 +++++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 19c9d38c82..a95068ab03 100644 --- a/package.json +++ b/package.json @@ -927,11 +927,11 @@ "cross-env": "3.1.3", "css-loader": "0.28.4", "detect-indent": "^5.0.0", - "electron": "1.8.6", + "electron": "2.0.2", "electron-builder": "^20.5.1", "electron-devtools-installer": "^2.2.3", "electron-mocha": "5.0.0", - "electron-rebuild": "1.6.0", + "electron-rebuild": "^1.7.3", "enzyme": "^3.3.0", "enzyme-adapter-react-16": "^1.1.1", "enzyme-to-json": "^3.3.1", @@ -955,6 +955,7 @@ "minimatch": "3.0.4", "mkdirp": "0.5.1", "mocha": "3.1.2", + "node-abi": "^2.4.1", "nyc": "^11.4.1", "oni-release-downloader": "^0.0.10", "opencollective": "1.0.3", diff --git a/test/common/runInProcTest.ts b/test/common/runInProcTest.ts index 851c1f8b5d..5cf235f2da 100644 --- a/test/common/runInProcTest.ts +++ b/test/common/runInProcTest.ts @@ -184,11 +184,15 @@ export const runInProcTest = ( writeLogs(rendererLogs) console.log("--- " + testName + " ---") - const mainProcessLogs: any[] = await oni.client.getMainProcessLogs() console.log("---LOGS (Main): " + testName) - mainProcessLogs.forEach(l => { - console.log(l) - }) + if (!result.passed) { + const mainProcessLogs: any[] = await oni.client.getMainProcessLogs() + mainProcessLogs.forEach(l => { + console.log(l) + }) + } else { + console.log("Skipping log output since test passed.") + } console.log("--- " + testName + " ---") console.log("") diff --git a/yarn.lock b/yarn.lock index 2016b35a21..c688f56fba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2269,9 +2269,9 @@ cli-spinners@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" -cli-spinners@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.1.0.tgz#f1847b168844d917a671eb9d147e3df497c90d06" +cli-spinners@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" cli-table@^0.3.1: version "0.3.1" @@ -3128,7 +3128,7 @@ detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" -detect-libc@^1.0.2: +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -3472,12 +3472,13 @@ electron-publish@20.5.0: lazy-val "^1.0.3" mime "^2.2.0" -electron-rebuild@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.6.0.tgz#e8d26f4d8e9fe5388df35864b3658e5cfd4dcb7e" +electron-rebuild@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.7.3.tgz#24ae06ad9dd61cb7e4d688961f49118c40a110eb" dependencies: colors "^1.1.2" debug "^2.6.3" + detect-libc "^1.0.3" fs-extra "^3.0.1" node-abi "^2.0.0" node-gyp "^3.6.0" @@ -3503,9 +3504,9 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@1.8.6: - version "1.8.6" - resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.6.tgz#6d45e377b321a35b78a794b64e40b2cf8face6ae" +electron@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.2.tgz#b77e05f83419cc5ec921a2d21f35b55e4bfc3d68" dependencies: "@types/node" "^8.0.24" electron-download "^3.0.1" @@ -6940,9 +6941,9 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -node-abi@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.2.tgz#4da6caceb6685fcd31e7dd1994ef6bb7d0a9c0b2" +node-abi@^2.0.0, node-abi@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.1.tgz#7628c4d4ec4e9cd3764ceb3652f36b2e7f8d4923" dependencies: semver "^5.4.1" @@ -7410,13 +7411,13 @@ ora@^0.2.3: object-assign "^4.0.1" ora@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-1.3.0.tgz#80078dd2b92a934af66a3ad72a5b910694ede51a" + version "1.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-1.4.0.tgz#884458215b3a5d4097592285f93321bb7a79e2e5" dependencies: - chalk "^1.1.1" + chalk "^2.1.0" cli-cursor "^2.1.0" - cli-spinners "^1.0.0" - log-symbols "^1.0.2" + cli-spinners "^1.0.1" + log-symbols "^2.1.0" original@>=0.0.5: version "1.0.0" @@ -7450,7 +7451,14 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@0, osenv@^0.1.4: +osenv@0: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +osenv@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" dependencies: @@ -8688,9 +8696,9 @@ request-promise-native@^1.0.3, request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@2, request@^2.45.0, request@^2.79.0, request@^2.83.0, request@~2.83.0: - version "2.83.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" +request@2: + version "2.86.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.86.0.tgz#2b9497f449b0a32654c081a5cf426bbfb5bf5b69" dependencies: aws-sign2 "~0.7.0" aws4 "^1.6.0" @@ -8710,7 +8718,6 @@ request@2, request@^2.45.0, request@^2.79.0, request@^2.83.0, request@~2.83.0: performance-now "^2.1.0" qs "~6.5.1" safe-buffer "^5.1.1" - stringstream "~0.0.5" tough-cookie "~2.3.3" tunnel-agent "^0.6.0" uuid "^3.1.0" @@ -8742,6 +8749,33 @@ request@2.81.0, request@~2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@^2.45.0, request@^2.79.0, request@^2.83.0, request@~2.83.0: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + request@^2.81.0: version "2.85.0" resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" @@ -8908,13 +8942,7 @@ rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" -rxjs@^5.1.1: - version "5.5.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3" - dependencies: - symbol-observable "^1.0.1" - -rxjs@^5.4.2, rxjs@^5.5.2: +rxjs@^5.1.1, rxjs@^5.4.2, rxjs@^5.5.2: version "5.5.10" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045" dependencies: @@ -9746,7 +9774,7 @@ symbol-observable@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" -symbol-observable@^1.0.1, symbol-observable@^1.0.3: +symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" From 3cc6ae27a3ded6b019ddfa5b14009ec2b6c953a8 Mon Sep 17 00:00:00 2001 From: Amadeus Folego Date: Mon, 28 May 2018 11:34:02 -0300 Subject: [PATCH 063/102] Add Akin909 to list of contributors --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9f0079d64b..18b7c35ee4 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ Oni is an independent project and is made possible by the support of some except * the [neovim team](https://neovim.io/), especially [justinmk](https://github.com/justinmk) and [tarruda](https://github.com/tarruda) - Oni would not be possible without their vision * [jordwalke](https://github.com/jordwalke) for his generous support, inspiration, and ideas. And React ;) * [keforbes](https://github.com/keforbes) for helping to get this project off the ground +* [Akin909](https://github.com/Akin909) for his extensive contributions * [tillarnold](https://github.com/tillarnold) for giving us the `oni` npm package name * [mhartington](https://github.com/mhartington) for his generous support * [badosu](https://github.com/badosu) for his support, contributions, and managing the AUR releases From da1122a22870cef8d37861fab6bccefda90ea768 Mon Sep 17 00:00:00 2001 From: Amadeus Folego Date: Mon, 28 May 2018 13:06:59 -0300 Subject: [PATCH 064/102] Add CrossR to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 18b7c35ee4..2f08ca0f0d 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Oni is an independent project and is made possible by the support of some except * [jordwalke](https://github.com/jordwalke) for his generous support, inspiration, and ideas. And React ;) * [keforbes](https://github.com/keforbes) for helping to get this project off the ground * [Akin909](https://github.com/Akin909) for his extensive contributions +* [CrossR](https://github.com/CrossR) for polishing features and configurations * [tillarnold](https://github.com/tillarnold) for giving us the `oni` npm package name * [mhartington](https://github.com/mhartington) for his generous support * [badosu](https://github.com/badosu) for his support, contributions, and managing the AUR releases From 642508603fd48b90461984edac7bb19f12516b0b Mon Sep 17 00:00:00 2001 From: Amadeus Folego Date: Mon, 28 May 2018 13:08:22 -0300 Subject: [PATCH 065/102] Add Cryza to contributors list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2f08ca0f0d..a80e794aae 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ Oni is an independent project and is made possible by the support of some except * [keforbes](https://github.com/keforbes) for helping to get this project off the ground * [Akin909](https://github.com/Akin909) for his extensive contributions * [CrossR](https://github.com/CrossR) for polishing features and configurations +* [Cryza](https://github.com/Cryza) for the webgl renderer * [tillarnold](https://github.com/tillarnold) for giving us the `oni` npm package name * [mhartington](https://github.com/mhartington) for his generous support * [badosu](https://github.com/badosu) for his support, contributions, and managing the AUR releases From d18087914c7aad1f3c65161145ff2150d194f41a Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 29 May 2018 13:26:13 -0700 Subject: [PATCH 066/102] Add Wayan Jimmy as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 6535b508e4..44a19961da 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -126,6 +126,7 @@ Thanks you to all our backers for making Oni possible! * Carlos Coves Prieto * Bryan Germann * James Herdman +* Wayan Jimmy From 7f62821778178f51d01d49aa54cf2478ec119f63 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 29 May 2018 16:55:23 -0700 Subject: [PATCH 067/102] Automation: Improve assert logging for automation (#2261) * Improve assert logging for automation * Fix lint issues --- browser/src/Services/Automation.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/browser/src/Services/Automation.ts b/browser/src/Services/Automation.ts index 7742e5bf94..255c0a4633 100644 --- a/browser/src/Services/Automation.ts +++ b/browser/src/Services/Automation.ts @@ -189,10 +189,21 @@ export class Automation implements OniApi.Automation.Api { this._getOrCreateTestContainer("automated-test-container"), ) - resultElement.textContent = JSON.stringify({ - passed, - exception: exception || null, - }) + if (exception && exception.code && exception.code === "ERR_ASSERTION") { + resultElement.textContent = JSON.stringify({ + passed, + exception, + expected: exception.expected, + actual: exception.actual, + message: exception.message, + operator: exception.operator, + }) + } else { + resultElement.textContent = JSON.stringify({ + passed, + exception: exception || null, + }) + } } private _createElement(className: string, parentElement: HTMLElement): HTMLDivElement { From a34e1d64ecf529f2d31f5f0bbb9cc33c4d19980e Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 29 May 2018 17:40:48 -0700 Subject: [PATCH 068/102] Implement NeovimEditor implementation of setTextOptions (#2262) --- browser/src/Editor/Editor.ts | 4 ++++ browser/src/Editor/NeovimEditor/NeovimEditor.tsx | 13 +++++++++++++ browser/src/Editor/OniEditor/OniEditor.tsx | 4 ++++ browser/src/Services/EditorManager.ts | 4 ++++ package.json | 2 +- yarn.lock | 6 +++--- 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/browser/src/Editor/Editor.ts b/browser/src/Editor/Editor.ts index ec63c108ba..5f03741a08 100644 --- a/browser/src/Editor/Editor.ts +++ b/browser/src/Editor/Editor.ts @@ -86,6 +86,10 @@ export class Editor extends Disposable implements Oni.Editor { return Promise.reject("Not implemented") } + public setTextOptions(options: Oni.EditorTextOptions): Promise { + return Promise.reject("Not implemented") + } + protected setMode(mode: Oni.Vim.Mode): void { if (mode !== this._currentMode) { this._currentMode = mode diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index b702148fb0..9bda898c7e 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -866,6 +866,19 @@ export class NeovimEditor extends Editor implements IEditor { await this._neovimInstance.request("nvim_call_atomic", [atomicCalls]) } + public async setTextOptions(textOptions: Oni.EditorTextOptions): Promise { + const { insertSpacesForTab, tabSize } = textOptions + if (insertSpacesForTab) { + await this._neovimInstance.command("set expandtab") + } else { + await this._neovimInstance.command("set noexpandtab") + } + + await this._neovimInstance.command( + `set tabstop=${tabSize} shiftwidth=${tabSize} softtabstop=${tabSize}`, + ) + } + public async openFile( file: string, openOptions: Oni.FileOpenOptions = Oni.DefaultFileOpenOptions, diff --git a/browser/src/Editor/OniEditor/OniEditor.tsx b/browser/src/Editor/OniEditor/OniEditor.tsx index 029190db14..0b9e330f92 100644 --- a/browser/src/Editor/OniEditor/OniEditor.tsx +++ b/browser/src/Editor/OniEditor/OniEditor.tsx @@ -246,6 +246,10 @@ export class OniEditor extends Utility.Disposable implements IEditor { return this._neovimEditor.setSelection(range) } + public async setTextOptions(textOptions: Oni.EditorTextOptions): Promise { + return this._neovimEditor.setTextOptions(textOptions) + } + public async blockInput( inputFunction: (input: Oni.InputCallbackFunction) => Promise, ): Promise { diff --git a/browser/src/Services/EditorManager.ts b/browser/src/Services/EditorManager.ts index 6488bd2ac1..e2642c9ff8 100644 --- a/browser/src/Services/EditorManager.ts +++ b/browser/src/Services/EditorManager.ts @@ -177,6 +177,10 @@ class AnyEditorProxy implements Oni.Editor { return this._activeEditor.getBuffers() } + public setTextOptions(options: Oni.EditorTextOptions): Promise { + return this._activeEditor.setTextOptions(options) + } + /** * Internal methods */ diff --git a/package.json b/package.json index a95068ab03..f8a22e170d 100644 --- a/package.json +++ b/package.json @@ -860,7 +860,7 @@ "minimist": "1.2.0", "msgpack-lite": "0.1.26", "ocaml-language-server": "^1.0.27", - "oni-api": "^0.0.44", + "oni-api": "^0.0.45", "oni-neovim-binaries": "0.1.1", "oni-ripgrep": "0.0.4", "oni-types": "^0.0.8", diff --git a/yarn.lock b/yarn.lock index c688f56fba..62cc9f727e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7323,9 +7323,9 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -oni-api@^0.0.44: - version "0.0.44" - resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.44.tgz#26e4999ba5da0be0e7f25b6705c2d5412fe4eb23" +oni-api@^0.0.45: + version "0.0.45" + resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.45.tgz#e9191fbc5069e01b3cbea644bc697be9aaf1d699" oni-neovim-binaries@0.1.1: version "0.1.1" From b3a31601b6cbddaccb160bbf4bfddf8395c7ded6 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Tue, 29 May 2018 18:29:53 -0700 Subject: [PATCH 069/102] Remove unused parser class (#2263) --- browser/src/Parser.ts | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 browser/src/Parser.ts diff --git a/browser/src/Parser.ts b/browser/src/Parser.ts deleted file mode 100644 index 4af2a8aa19..0000000000 --- a/browser/src/Parser.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as fs from "fs" - -export function parseJsonFromFile(file: string): T { - return JSON.parse(fs.readFileSync(file, "utf8")) -} From 74c689f7a6e939606c72ffa90828c6663d41765b Mon Sep 17 00:00:00 2001 From: Akin Date: Thu, 31 May 2018 12:27:09 +0100 Subject: [PATCH 070/102] Fix #2185 - Remove call to set scrollbind (#2270) This PR removes the scroll binding of the reference buffer, fixes #2185. This is the only point in the app where we call `scrollbind` so removing it should resolve this issue. --- browser/src/Editor/BufferManager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/src/Editor/BufferManager.ts b/browser/src/Editor/BufferManager.ts index e12492dfb2..7d1e044817 100644 --- a/browser/src/Editor/BufferManager.ts +++ b/browser/src/Editor/BufferManager.ts @@ -214,7 +214,6 @@ export class Buffer implements IBuffer { ["nvim_command", ["setlocal noswapfile"]], ["nvim_command", ["setlocal nobuflisted"]], ["nvim_command", ["setlocal nomodifiable"]], - ["nvim_command", ["windo set scrollbind!"]], ] const [result, error] = await this._neovimInstance.request( From 4199a6f7fe22f3907967b420d72edf67fe732f2e Mon Sep 17 00:00:00 2001 From: Nick Neisen Date: Thu, 31 May 2018 15:06:20 -0600 Subject: [PATCH 071/102] Change explorer file delete to (#2265) --- browser/src/Input/KeyBindings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/src/Input/KeyBindings.ts b/browser/src/Input/KeyBindings.ts index f328bc0079..e834c648aa 100644 --- a/browser/src/Input/KeyBindings.ts +++ b/browser/src/Input/KeyBindings.ts @@ -134,7 +134,7 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati // Explorer input.bind("d", "explorer.delete.persist", isExplorerActive) input.bind("", "explorer.delete.persist", isExplorerActive) - input.bind("", "explorer.delete", isExplorerActive) + input.bind("", "explorer.delete", isExplorerActive) input.bind("", "explorer.delete", isExplorerActive) input.bind("y", "explorer.yank", isExplorerActive) input.bind("p", "explorer.paste", isExplorerActive) From e95d70b78fff90726848fe1a560d24f3857b076e Mon Sep 17 00:00:00 2001 From: Amadeus Folego Date: Fri, 1 Jun 2018 14:32:26 -0300 Subject: [PATCH 072/102] Improve notification dimensions (#2244) Use relative units for positioning and also increase max-size of notifications. Fixes #2233 ![screenshot from 2018-05-24 07-41-06](https://user-images.githubusercontent.com/347552/40480807-f1c5ce56-5f25-11e8-893c-ff9deb8085ba.png) --- browser/src/Services/Notifications/NotificationsView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/src/Services/Notifications/NotificationsView.tsx b/browser/src/Services/Notifications/NotificationsView.tsx index f993eeaac7..5bf7cf6a2f 100644 --- a/browser/src/Services/Notifications/NotificationsView.tsx +++ b/browser/src/Services/Notifications/NotificationsView.tsx @@ -35,10 +35,10 @@ const Transition = (props: { children: React.ReactNode }) => { const NotificationsWrapper = styled.div` position: absolute; - top: 16px; - right: 16px; + top: 1em; + right: 1em; max-height: 90%; - max-width: 25rem; + max-width: 33vw; pointer-events: all; overflow: auto; From 9b146396aea71f2285d14d51a82abecffea46cca Mon Sep 17 00:00:00 2001 From: Jeffrey Tao Date: Sat, 2 Jun 2018 14:25:03 -0400 Subject: [PATCH 073/102] Fix empty item in QuickOpen (#2278) * Remove empty string from Quick Open results * FinderProcess data logic refactor * FinderProcess extractSplitData tests --- .../src/Services/QuickOpen/FinderProcess.ts | 50 ++++++++++++---- .../Services/QuickOpen/FinderProcessTests.ts | 60 +++++++++++++++++++ 2 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 browser/test/Services/QuickOpen/FinderProcessTests.ts diff --git a/browser/src/Services/QuickOpen/FinderProcess.ts b/browser/src/Services/QuickOpen/FinderProcess.ts index 4fd64b8f9b..250853cc1d 100644 --- a/browser/src/Services/QuickOpen/FinderProcess.ts +++ b/browser/src/Services/QuickOpen/FinderProcess.ts @@ -48,22 +48,17 @@ export class FinderProcess { cwd: this._workspace.activeWorkspace, }) this._process.stdout.on("data", data => { - if (!data) { - return - } - - const dataString = data.toString() - const isCleanEnd = dataString.endsWith(this._splitCharacter) - const splitData = dataString.split(this._splitCharacter) + const { didExtract, remnant, splitData } = extractSplitData( + data, + this._splitCharacter, + this._lastData, + ) - if (this._lastData && splitData.length > 0) { - splitData[0] = this._lastData + splitData[0] - this._lastData = "" + if (!didExtract) { + return } - if (!isCleanEnd) { - this._lastData = splitData.pop() - } + this._lastData = remnant this._onData.dispatch(splitData) }) @@ -81,3 +76,32 @@ export class FinderProcess { this._process.kill() } } + +export const extractSplitData = ( + data: string | Buffer, + splitCharacter: string, + lastRemnant: string, +) => { + if (!data) { + return { + didExtract: false, + remnant: "", + splitData: [], + } + } + + const dataString = lastRemnant + data.toString() + const isCleanEnd = dataString.endsWith(splitCharacter) + const splitData = dataString.split(splitCharacter) + + let remnant = "" + + if (!isCleanEnd) { + remnant = splitData.pop() + } else { + // split leaves behind an empty string in the array if the string to split ends with the delimiter + splitData.splice(-1, 1) + } + + return { didExtract: true, remnant, splitData } +} diff --git a/browser/test/Services/QuickOpen/FinderProcessTests.ts b/browser/test/Services/QuickOpen/FinderProcessTests.ts new file mode 100644 index 0000000000..1ea1e975e8 --- /dev/null +++ b/browser/test/Services/QuickOpen/FinderProcessTests.ts @@ -0,0 +1,60 @@ +/** + * FinderProcessTests.ts + */ + +import * as assert from "assert" +import { extractSplitData } from "../../../src/Services/QuickOpen/FinderProcess" + +describe("extractSplitData", () => { + it("Splits the data by the delimiter", () => { + const data = "file1\nfile2\nfile3\n" + const delimiter = "\n" + const lastRemnant = "" + + const { splitData } = extractSplitData(data, delimiter, lastRemnant) + + assert.equal(splitData.length, 3) + }) + + it("Ignores empty input", () => { + const data = "" + const delimiter = "\n" + const lastRemnant = "" + + const { didExtract } = extractSplitData(data, delimiter, lastRemnant) + + assert.equal(didExtract, false) + }) + + it("Returns a remnant if the data doesn't end with the delimiter", () => { + const data = "file1\nfile2" + const delimiter = "\n" + const lastRemnant = "" + + const { remnant, splitData } = extractSplitData(data, delimiter, lastRemnant) + + assert.equal(remnant, "file2") + assert.equal(splitData.length, 1) + }) + + it("Returns an empty remnant if the data does end with the delimiter", () => { + const data = "file1\nfile2\n" + const delimiter = "\n" + const lastRemnant = "" + + const { remnant } = extractSplitData(data, delimiter, lastRemnant) + + assert.equal(remnant, "") + }) + + it("Prepends the last remnant if there was one", () => { + const data = "e1\nfile2\n" + const delimiter = "\n" + const lastRemnant = "fil" + + const { splitData } = extractSplitData(data, delimiter, lastRemnant) + + assert.equal(splitData.length, 2) + assert.equal(splitData[0], "file1") + }) +}) From cd87a1f201ea8b20659c7d76d3a57464ce186cb8 Mon Sep 17 00:00:00 2001 From: Yohan Lee Date: Sun, 3 Jun 2018 06:19:44 -0400 Subject: [PATCH 074/102] Feature/ Add keyboard shortcut to edit oni config (#2280) Issue #2276 This change allows pressing `CMD+,` (Mac) to open oni config file. Keystroke is `CTRL+,` on Windows and Linux platforms. screen shot 2018-06-02 at 5 13 32 pm ![open config](https://user-images.githubusercontent.com/9834975/40880978-5de4bb96-6689-11e8-9bf5-51b2b4433b45.gif) --- browser/src/Input/KeyBindings.ts | 2 ++ main/src/menu.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/browser/src/Input/KeyBindings.ts b/browser/src/Input/KeyBindings.ts index e834c648aa..3f5a26d1b2 100644 --- a/browser/src/Input/KeyBindings.ts +++ b/browser/src/Input/KeyBindings.ts @@ -47,6 +47,7 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati input.bind("", "oni.editor.hide") input.bind("", "buffer.toggle") input.bind("", "search.searchAllFiles") + input.bind("", "oni.config.openConfigJs") if (config.getValue("editor.clipboard.enabled")) { input.bind("", "editor.clipboard.yank", isVisualMode) @@ -66,6 +67,7 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati input.bind("", "language.symbols.document") input.bind("", "buffer.toggle") input.bind("", "search.searchAllFiles") + input.bind("", "oni.config.openConfigJs") if (config.getValue("editor.clipboard.enabled")) { input.bind("", "editor.clipboard.yank", isVisualMode) diff --git a/main/src/menu.ts b/main/src/menu.ts index 3f65de3f67..11e49f4d0d 100644 --- a/main/src/menu.ts +++ b/main/src/menu.ts @@ -67,6 +67,7 @@ export const buildMenu = (mainWindow, loadInit) => { submenu: [ { label: "Edit Oni config", + accelerator: "CmdOrCtrl+,", click(item, focusedWindow) { executeOniCommand(focusedWindow, "oni.config.openConfigJs") }, @@ -77,6 +78,7 @@ export const buildMenu = (mainWindow, loadInit) => { if (loadInit) { preferences.submenu.push({ label: "Edit Neovim config", + accelerator: null, click(item, focusedWindow) { executeOniCommand(focusedWindow, "oni.config.openInitVim") }, From 0051335f72caa16c8b54a7df0066edc8e24c0d25 Mon Sep 17 00:00:00 2001 From: Akin Date: Mon, 4 Jun 2018 20:39:33 +0100 Subject: [PATCH 075/102] Feature/ convert quickinfo to styled components (#2277) * convert quickinfo tooltip to styled components * remove quick info less file, switch some components to SFCs recreate QuickInfo classes as styled-components * fix lint errors * convert Errors info to stateless functional component * move errors our of mount of errorInfo * add test for error info component * add snapshot test for error info component * actually add snapshot * add snapshot for text components * refactor to SFC add very minimal snapsthot and visiblity test * update snapshots --- browser/src/CSS.ts | 1 - .../src/Editor/NeovimEditor/HoverRenderer.tsx | 90 ++++++++++--------- .../src/Editor/NeovimEditor/NeovimEditor.tsx | 7 +- .../Services/Language/SignatureHelpView.tsx | 51 +++++------ browser/src/UI/components/ErrorInfo.tsx | 55 +++++++----- browser/src/UI/components/QuickInfo.less | 27 ------ browser/src/UI/components/QuickInfo.tsx | 10 +++ .../src/UI/components/QuickInfoContainer.tsx | 35 ++++---- browser/src/UI/components/Text.tsx | 19 ++-- browser/src/UI/components/WindowTitle.tsx | 54 +++++------ package.json | 3 +- ui-tests/ErrorInfo.test.tsx | 60 +++++++++++++ ui-tests/Text.test.tsx | 15 ++++ ui-tests/WindowTitleView.test.tsx | 15 ++++ .../__snapshots__/ErrorInfo.test.tsx.snap | 44 +++++++++ ui-tests/__snapshots__/Text.test.tsx.snap | 13 +++ .../WindowTitleView.test.tsx.snap | 10 +++ yarn.lock | 16 +++- 18 files changed, 340 insertions(+), 185 deletions(-) delete mode 100644 browser/src/UI/components/QuickInfo.less create mode 100644 ui-tests/ErrorInfo.test.tsx create mode 100644 ui-tests/Text.test.tsx create mode 100644 ui-tests/WindowTitleView.test.tsx create mode 100644 ui-tests/__snapshots__/ErrorInfo.test.tsx.snap create mode 100644 ui-tests/__snapshots__/Text.test.tsx.snap create mode 100644 ui-tests/__snapshots__/WindowTitleView.test.tsx.snap diff --git a/browser/src/CSS.ts b/browser/src/CSS.ts index 3c486fe9c8..99458547d2 100644 --- a/browser/src/CSS.ts +++ b/browser/src/CSS.ts @@ -13,6 +13,5 @@ export const activate = () => { require("./Services/Menu/Menu.less") require("./UI/components/InstallHelp.less") - require("./UI/components/QuickInfo.less") require("./UI/components/Tabs.less") } diff --git a/browser/src/Editor/NeovimEditor/HoverRenderer.tsx b/browser/src/Editor/NeovimEditor/HoverRenderer.tsx index 9194533646..893acd13b9 100644 --- a/browser/src/Editor/NeovimEditor/HoverRenderer.tsx +++ b/browser/src/Editor/NeovimEditor/HoverRenderer.tsx @@ -8,23 +8,26 @@ import * as React from "react" import * as types from "vscode-languageserver-types" import getTokens from "./../../Services/SyntaxHighlighting/TokenGenerator" +import { enableMouse } from "./../../UI/components/common" import { ErrorInfo } from "./../../UI/components/ErrorInfo" +import { QuickInfoElement, QuickInfoWrapper } from "./../../UI/components/QuickInfo" import QuickInfoWithTheme from "./../../UI/components/QuickInfoContainer" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" -import { IColors } from "./../../Services/Colors" import { Configuration } from "./../../Services/Configuration" import { convertMarkdown } from "./markdown" -import * as Selectors from "./NeovimEditorSelectors" import { IToolTipsProvider } from "./ToolTipsProvider" const HoverToolTipId = "hover-tool-tip" +const HoverRendererContainer = QuickInfoWrapper.extend` + ${enableMouse}; +` + export class HoverRenderer { constructor( - private _colors: IColors, private _editor: Oni.Editor, private _configuration: Configuration, private _toolTipsProvider: IToolTipsProvider, @@ -58,42 +61,34 @@ export class HoverRenderer { errors: types.Diagnostic[], ): Promise { const titleAndContents = await getTitleAndContents(hover) - const quickInfoElement = !!titleAndContents ? ( - - ) : null - - const borderColor = this._colors.getColor("toolTip.border") - - let customErrorStyle = {} - if (quickInfoElement) { - // TODO: - customErrorStyle = { - "border-bottom": "1px solid " + borderColor, - } - } - - const errorElements = getErrorElements(errors, customErrorStyle) - let debugScopeElement: JSX.Element = null - - if (this._configuration.getValue("editor.textMateHighlighting.debugScopes")) { - debugScopeElement = this._getDebugScopesElement() - } - - // Remove falsy values as check below [null] is truthy - const elements = [...errorElements, quickInfoElement, debugScopeElement].filter(Boolean) + const showDebugScope = this._configuration.getValue( + "editor.textMateHighlighting.debugScopes", + ) - if (!elements.length) { - return null - } + const errorsExist = Boolean(errors && errors.length) + const contentExists = Boolean(errorsExist || titleAndContents || showDebugScope) return ( -
-
-
-
{elements}
-
-
-
+ contentExists && ( + + +
+
+ + + {showDebugScope && this._getDebugScopesElement()} +
+
+
+
+ ) ) } @@ -113,7 +108,8 @@ export class HoverRenderer { if (!scopeInfo || !scopeInfo.scopes) { return null } - const items = scopeInfo.scopes.map((si: string) =>
  • {si}
  • ) + + const items = scopeInfo.scopes.map((si: string) =>
  • {si}
  • ) return (
    DEBUG: TextMate Scopes:
    -
      {items}
    +
      {items}
    ) } @@ -129,12 +125,18 @@ export class HoverRenderer { const html = (str: string) => ({ __html: str }) -const getErrorElements = (errors: types.Diagnostic[], style: any): JSX.Element[] => { - if (!errors || !errors.length) { - return Selectors.EmptyArray - } else { - return [] - } +interface ErrorElementProps { + errors: types.Diagnostic[] + hasQuickInfo: boolean + isVisible: boolean +} + +const ErrorElement = ({ isVisible, errors, hasQuickInfo }: ErrorElementProps) => { + return ( + isVisible && ( + + ) + ) } const getTitleAndContents = async (result: types.Hover) => { diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index 9bda898c7e..c42d6df779 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -206,12 +206,7 @@ export class NeovimEditor extends Editor implements IEditor { this._bufferManager = new BufferManager(this._neovimInstance, this._actions, this._store) this._screen = new NeovimScreen() - this._hoverRenderer = new HoverRenderer( - this._colors, - this, - this._configuration, - this._toolTipsProvider, - ) + this._hoverRenderer = new HoverRenderer(this, this._configuration, this._toolTipsProvider) this._definition = new Definition(this, this._store) this._symbols = new Symbols( diff --git a/browser/src/Services/Language/SignatureHelpView.tsx b/browser/src/Services/Language/SignatureHelpView.tsx index 4356f308b3..6fe776500f 100644 --- a/browser/src/Services/Language/SignatureHelpView.tsx +++ b/browser/src/Services/Language/SignatureHelpView.tsx @@ -2,20 +2,15 @@ import * as React from "react" import * as types from "vscode-languageserver-types" -import { QuickInfoDocumentation, Title } from "./../../UI/components/QuickInfo" +import { + QuickInfoDocumentation, + QuickInfoElement, + QuickInfoWrapper, + Title, +} from "./../../UI/components/QuickInfo" import { SelectedText, Text } from "./../../UI/components/Text" -export class SignatureHelpView extends React.PureComponent { - public render(): JSX.Element { - return ( -
    -
    {getElementsFromType(this.props)}
    -
    - ) - } -} - -export const getElementsFromType = (signatureHelp: types.SignatureHelp): JSX.Element[] => { +export const getElementsFromType = (signatureHelp: types.SignatureHelp): JSX.Element => { const elements = [] const currentItem = signatureHelp.signatures[signatureHelp.activeSignature] @@ -62,21 +57,27 @@ export const getElementsFromType = (signatureHelp: types.SignatureHelp): JSX.Ele elements.push() - const titleContents = [ - - {elements} - , - ] - const selectedIndex = Math.min(currentItem.parameters.length, signatureHelp.activeParameter) const selectedArgument = currentItem.parameters[selectedIndex] - if (selectedArgument && selectedArgument.documentation) { - titleContents.push() - } - return titleContents + return ( + + + {elements} + + {!!(selectedArgument && selectedArgument.documentation) && ( + + )} + + ) } -export const render = (signatureHelp: types.SignatureHelp) => { - return -} +export const SignatureHelpView = (props: types.SignatureHelp) => ( + + {getElementsFromType(props)} + +) + +export const render = (signatureHelp: types.SignatureHelp) => ( + +) diff --git a/browser/src/UI/components/ErrorInfo.tsx b/browser/src/UI/components/ErrorInfo.tsx index b197a0185c..cfa84e27a9 100644 --- a/browser/src/UI/components/ErrorInfo.tsx +++ b/browser/src/UI/components/ErrorInfo.tsx @@ -1,37 +1,50 @@ import * as React from "react" import * as types from "vscode-languageserver-types" +import styled from "./common" import { ErrorIcon } from "./Error" import { getColorFromSeverity } from "./../../Services/Diagnostics" export interface IErrorInfoProps { - style: React.CSSProperties + hasQuickInfo: boolean errors: types.Diagnostic[] } +export const DiagnosticMessage = styled.span` + margin-left: 1em; +` + +type StyleProps = Pick + +const DiagnosticContainer = styled("div")` + user-select: none; + cursor: default; + border-bottom: ${p => (p.hasQuickInfo ? `1px solid ${p.theme["toolTip.border"]}` : "")}; +` + +export const Diagnostic = styled.div` + margin: 8px; + display: flex; + flex-direction: row; +` + /** * Helper component to render errors in the QuickInfo bubble */ -export class ErrorInfo extends React.PureComponent { - public render(): null | JSX.Element { - if (!this.props.errors) { - return null - } - - const errs = this.props.errors.map(e => ( -
    - - {e.message} -
    - )) - - const style = this.props.style || {} - - return ( -
    - {errs} -
    +export const ErrorInfo = (props: IErrorInfoProps) => { + return ( + props.errors && ( + + {props.errors.map((e, idx) => ( + + + + {e.message} + + + ))} + ) - } + ) } diff --git a/browser/src/UI/components/QuickInfo.less b/browser/src/UI/components/QuickInfo.less deleted file mode 100644 index 57fb8482ce..0000000000 --- a/browser/src/UI/components/QuickInfo.less +++ /dev/null @@ -1,27 +0,0 @@ -@import (reference) "./common.less"; - -.quickinfo-container { - -webkit-user-select: none; - cursor: default; - - .quickinfo { - text-overflow: ellipsis; - overflow: hidden; - - .diagnostic { - margin: 8px; - - display: flex; - flex-direction: row; - - .icon-container { - margin-right: 8px; - } - } - - .selected { - font-style: italic; - text-decoration: underline; - } - } -} diff --git a/browser/src/UI/components/QuickInfo.tsx b/browser/src/UI/components/QuickInfo.tsx index 2267540852..15a94331c8 100644 --- a/browser/src/UI/components/QuickInfo.tsx +++ b/browser/src/UI/components/QuickInfo.tsx @@ -3,6 +3,16 @@ import * as os from "os" import * as React from "react" import styled, { boxShadowInset, css, fontSizeSmall, withProps } from "./common" +export const QuickInfoWrapper = styled.div` + user-select: none; + cursor: default; +` + +export const QuickInfoElement = styled.div` + text-overflow: ellipsis; + overflow: hidden; +` + const codeBlockStyle = css` color: ${p => p.theme.foreground}; padding: 0.4em 0.4em 0.4em 0.4em; diff --git a/browser/src/UI/components/QuickInfoContainer.tsx b/browser/src/UI/components/QuickInfoContainer.tsx index 08679cb681..936c28a211 100644 --- a/browser/src/UI/components/QuickInfoContainer.tsx +++ b/browser/src/UI/components/QuickInfoContainer.tsx @@ -6,6 +6,7 @@ import TokenThemeProvider from "./../../Services/SyntaxHighlighting/TokenThemePr interface IQuickInfoProps { titleAndContents: ITitleAndContents + isVisible: boolean } interface ITitleAndContents { @@ -19,7 +20,7 @@ interface ITitleAndContents { class QuickInfoHoverContainer extends React.Component { public render() { - const { titleAndContents } = this.props + const { titleAndContents, isVisible } = this.props const hasTitle = !!(titleAndContents && titleAndContents.title.__html) const hasDocs = hasTitle && @@ -30,23 +31,25 @@ class QuickInfoHoverContainer extends React.Component { ) return ( - ( - - - {titleAndContents.description && ( - ( + + - )} - - )} - /> + {titleAndContents.description && ( + + )} + + )} + /> + ) ) } } diff --git a/browser/src/UI/components/Text.tsx b/browser/src/UI/components/Text.tsx index 4ca8764f34..2a131999f1 100644 --- a/browser/src/UI/components/Text.tsx +++ b/browser/src/UI/components/Text.tsx @@ -1,19 +1,16 @@ import * as React from "react" +import styled from "./common" + export interface ITextProps { text: string } -export class TextComponent extends React.PureComponent {} +const Selected = styled.span` + font-style: italic; + text-decoration: underline; +` -export class Text extends TextComponent { - public render(): JSX.Element { - return {this.props.text} - } -} +export const Text = (props: ITextProps) => {props.text} -export class SelectedText extends TextComponent { - public render(): JSX.Element { - return {this.props.text} - } -} +export const SelectedText = (props: ITextProps) => {props.text} diff --git a/browser/src/UI/components/WindowTitle.tsx b/browser/src/UI/components/WindowTitle.tsx index 824bfaaa40..26a5ceb143 100644 --- a/browser/src/UI/components/WindowTitle.tsx +++ b/browser/src/UI/components/WindowTitle.tsx @@ -10,42 +10,38 @@ import { connect } from "react-redux" import { commandManager } from "./../../Services/CommandManager" import * as State from "./../Shell/ShellState" +import styled from "./common" export interface IWindowTitleViewProps { visible: boolean title: string - backgroundColor: string - foregroundColor: string } -export class WindowTitleView extends React.PureComponent { - public render(): null | JSX.Element { - if (!this.props.visible) { - return null - } - - const style: React.CSSProperties = { - height: "22px", - lineHeight: "22px", - zoom: 1, // Don't allow this to be impacted by zoom - backgroundColor: this.props.backgroundColor, - color: this.props.foregroundColor, - textAlign: "center", - WebkitAppRegion: "drag", - WebkitUserSelect: "none", - pointerEvents: "all", - } +const WindowTitleContainer = styled.div` + height: 22px; + padding: 3px 0; + line-height: 22px; + zoom: 1; /* Dont allow this to be impacted by zoom */ + background-color: ${p => p.theme["title.background"]}; + color: ${p => p.theme["title.foreground"]}; + text-align: center; + -webkit-app-region: drag; + user-select: none; + pointer-events: all; +` + +const onDoubleClick = () => { + commandManager.executeCommand("oni.editor.maximize") +} - return ( -
    - {this.props.title} -
    +export const WindowTitleView = (props: IWindowTitleViewProps) => { + return ( + props.visible && ( + + {props.title} + ) - } - - private onDoubleClick() { - commandManager.executeCommand("oni.editor.maximize") - } + ) } export interface IWindowTitleProps { @@ -59,8 +55,6 @@ export const mapStateToProps = ( return { visible: props.visible && !state.isFullScreen, title: state.windowTitle, - backgroundColor: state.colors["title.background"], - foregroundColor: state.colors["title.foreground"], } } diff --git a/package.json b/package.json index f8a22e170d..3518483696 100644 --- a/package.json +++ b/package.json @@ -900,9 +900,10 @@ "@types/mocha": "2.2.33", "@types/msgpack-lite": "0.1.4", "@types/node": "8.0.53", + "@types/react": "^16.3.16", "@types/react-dnd": "^2.0.34", "@types/react-dnd-html5-backend": "^2.1.8", - "@types/react-dom": "16.0.3", + "@types/react-dom": "^16.0.5", "@types/react-motion": "0.0.23", "@types/react-redux": "5.0.12", "@types/react-test-renderer": "^16.0.0", diff --git a/ui-tests/ErrorInfo.test.tsx b/ui-tests/ErrorInfo.test.tsx new file mode 100644 index 0000000000..3aff74cb0a --- /dev/null +++ b/ui-tests/ErrorInfo.test.tsx @@ -0,0 +1,60 @@ +import * as React from "react" +import { shallow } from "enzyme" +import { Diagnostic } from "vscode-languageserver-types" + +import { ErrorInfo, DiagnosticMessage } from "../browser/src/UI/components/ErrorInfo" + +describe("", () => { + const errors: Diagnostic[] = [ + { + range: { + start: { + line: 0, + character: 0, + }, + end: { + line: 0, + character: 10, + }, + }, + severity: 2, + code: 5, + source: "test", + message: "an error here", + }, + ] + + it("Should Render without crashing", () => { + const wrapper = shallow() + expect(wrapper.length).toBe(1) + }) + it("Should Not Render without Errors", () => { + const wrapper = shallow() + expect(wrapper.children().length).toBe(0) + }) + it("Should render the correct diagnostic message", () => { + const wrapper = shallow() + expect(wrapper.dive().find("[data-id='diagnostic-message']").length).toEqual(1) + expect( + wrapper + .dive() + .find("[data-id='diagnostic-message']") + .dive() + .text(), + ).toBe(errors[0].message) + }) + + it("Should render the correct number of diagnostics", () => { + const wrapper = shallow( + , + ) + expect(wrapper.dive().find("[data-id='diagnostic-message']").length).toBe(3) + }) + + it("Should match the last snapshot on record unless a purposeful change was made", () => { + const wrapper = shallow( + , + ) + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/ui-tests/Text.test.tsx b/ui-tests/Text.test.tsx new file mode 100644 index 0000000000..6bfbcbe11d --- /dev/null +++ b/ui-tests/Text.test.tsx @@ -0,0 +1,15 @@ +import * as React from "react" +import { shallow } from "enzyme" + +import { SelectedText, Text } from "./../browser/src/UI/components/Text" + +describe("", () => { + it("Text component should match the last snapshot on record", () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) + it("SelectedText component should match the last snapshot on record", () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/ui-tests/WindowTitleView.test.tsx b/ui-tests/WindowTitleView.test.tsx new file mode 100644 index 0000000000..56e2f09800 --- /dev/null +++ b/ui-tests/WindowTitleView.test.tsx @@ -0,0 +1,15 @@ +import * as React from "react" +import { shallow } from "enzyme" + +import { WindowTitleView } from "./../browser/src/UI/components/WindowTitle" + +describe("", () => { + it("Text component should match the last snapshot on record", () => { + const wrapper = shallow() + expect(wrapper).toMatchSnapshot() + }) + it("should only render if visible", () => { + const wrapper = shallow() + expect(wrapper.children().length).toBe(0) + }) +}) diff --git a/ui-tests/__snapshots__/ErrorInfo.test.tsx.snap b/ui-tests/__snapshots__/ErrorInfo.test.tsx.snap new file mode 100644 index 0000000000..59ec99e0af --- /dev/null +++ b/ui-tests/__snapshots__/ErrorInfo.test.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` Should match the last snapshot on record unless a purposeful change was made 1`] = ` + + + + + an error here + + + + + + an error here + + + + + + an error here + + + +`; diff --git a/ui-tests/__snapshots__/Text.test.tsx.snap b/ui-tests/__snapshots__/Text.test.tsx.snap new file mode 100644 index 0000000000..e312b41967 --- /dev/null +++ b/ui-tests/__snapshots__/Text.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` SelectedText component should match the last snapshot on record 1`] = ` + + Test + +`; + +exports[` Text component should match the last snapshot on record 1`] = ` + + Test + +`; diff --git a/ui-tests/__snapshots__/WindowTitleView.test.tsx.snap b/ui-tests/__snapshots__/WindowTitleView.test.tsx.snap new file mode 100644 index 0000000000..fc1bc1038e --- /dev/null +++ b/ui-tests/__snapshots__/WindowTitleView.test.tsx.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` Text component should match the last snapshot on record 1`] = ` + + Test Window + +`; diff --git a/yarn.lock b/yarn.lock index 62cc9f727e..3f77e84f6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -184,9 +184,9 @@ dependencies: "@types/react" "*" -"@types/react-dom@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64" +"@types/react-dom@^16.0.5": + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.5.tgz#a757457662e3819409229e8f86795ff37b371f96" dependencies: "@types/node" "*" "@types/react" "*" @@ -227,6 +227,12 @@ version "16.0.25" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.25.tgz#bf696b83fe480c5e0eff4335ee39ebc95884a1ed" +"@types/react@^16.3.16": + version "16.3.16" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.16.tgz#78fc44a90b45701f50c8a7008f733680ba51fc86" + dependencies: + csstype "^2.2.0" + "@types/redux-batched-subscribe@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/redux-batched-subscribe/-/redux-batched-subscribe-0.1.2.tgz#a5f0e510b07b6e8f10fe781bd830fae4f9bcf648" @@ -2938,6 +2944,10 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.3.tgz#2504152e6e1cc59b32098b7f5d6a63f16294c1f7" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" From d773fa040eafcaa565827646d1d49b8ef995a0c1 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Mon, 4 Jun 2018 14:46:09 -0700 Subject: [PATCH 076/102] Fix #2163 - Override persisted workspace if folder passed in (#2264) * Override persisted workspace if specified by file / folder * Fix logging --- browser/src/App.ts | 22 +++++++++++++++++++-- browser/src/Services/Workspace/Workspace.ts | 8 ++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/browser/src/App.ts b/browser/src/App.ts index 3e1b8cf0cf..ec225a1046 100644 --- a/browser/src/App.ts +++ b/browser/src/App.ts @@ -5,6 +5,7 @@ */ import { ipcRenderer } from "electron" +import * as fs from "fs" import * as minimist from "minimist" import * as path from "path" @@ -100,10 +101,27 @@ export const start = async (args: string[]): Promise => { const parsedArgs = minimist(args) const currentWorkingDirectory = process.cwd() - const filesToOpen = parsedArgs._.map( + const normalizedFiles = parsedArgs._.map( arg => (path.isAbsolute(arg) ? arg : path.join(currentWorkingDirectory, arg)), ) + const filesToOpen = normalizedFiles.filter(f => fs.existsSync(f) && fs.statSync(f).isFile()) + const foldersToOpen = normalizedFiles.filter( + f => fs.existsSync(f) && fs.statSync(f).isDirectory(), + ) + + Log.info("Files to open: " + JSON.stringify(filesToOpen)) + Log.info("Folders to open: " + JSON.stringify(foldersToOpen)) + + let workspaceToLoad = null + + // If a folder has been specified, we'll change directory to it + if (foldersToOpen.length > 0) { + workspaceToLoad = foldersToOpen[0] + } else if (filesToOpen.length > 0) { + workspaceToLoad = path.dirname(filesToOpen[0]) + } + // Helper for debugging: Performance.startMeasure("Oni.Start.Config") @@ -167,7 +185,7 @@ export const start = async (args: string[]): Promise => { const { editorManager } = await editorManagerPromise const Workspace = await workspacePromise - Workspace.activate(configuration, editorManager) + Workspace.activate(configuration, editorManager, workspaceToLoad) const workspace = Workspace.getInstance() const WindowManager = await windowManagerPromise diff --git a/browser/src/Services/Workspace/Workspace.ts b/browser/src/Services/Workspace/Workspace.ts index 2d42148a9a..8d33243fad 100644 --- a/browser/src/Services/Workspace/Workspace.ts +++ b/browser/src/Services/Workspace/Workspace.ts @@ -183,12 +183,16 @@ export class Workspace implements IWorkspace { let _workspace: Workspace = null let _workspaceConfiguration: WorkspaceConfiguration = null -export const activate = (configuration: Configuration, editorManager: EditorManager): void => { +export const activate = ( + configuration: Configuration, + editorManager: EditorManager, + workspaceToLoad?: string, +): void => { _workspace = new Workspace(editorManager, configuration) _workspaceConfiguration = new WorkspaceConfiguration(configuration, _workspace) - const defaultWorkspace = configuration.getValue("workspace.defaultWorkspace") + const defaultWorkspace = workspaceToLoad || configuration.getValue("workspace.defaultWorkspace") if (defaultWorkspace) { _workspace.changeDirectory(defaultWorkspace) From e1dd0cd5efa377f76982368fb432406f3a0fe39e Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Mon, 4 Jun 2018 18:11:30 -0700 Subject: [PATCH 077/102] Add Alex as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 44a19961da..5755f6502c 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -127,6 +127,7 @@ Thanks you to all our backers for making Oni possible! * Bryan Germann * James Herdman * Wayan Jimmy +* Alex From e46c720fc82f1466347016bc0916a8d744c94793 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Mon, 4 Jun 2018 19:18:40 -0700 Subject: [PATCH 078/102] Modularization: Switch to 'oni-core-logging' (#2283) * Start migrating from Log.ts -> oni-core-logging * Refactor remaining references * Fix lint issues --- browser/src/App.ts | 2 +- browser/src/Editor/BufferManager.ts | 2 +- .../src/Editor/NeovimEditor/NeovimEditor.tsx | 3 +- browser/src/Editor/NeovimEditor/Rename.tsx | 2 +- browser/src/Editor/NeovimEditor/markdown.ts | 2 +- browser/src/Editor/OniEditor/OniEditor.tsx | 2 +- browser/src/Input/Keyboard/KeyboardLayout.ts | 2 +- .../src/Input/Keyboard/KeyboardResolver.ts | 3 +- browser/src/Log.ts | 61 ------------------- browser/src/PeriodicJobs.ts | 2 +- browser/src/PersistentStore.ts | 2 +- .../LanguageClient/LanguageClientLogger.ts | 2 +- browser/src/Plugins/Api/Oni.ts | 3 +- browser/src/Plugins/Api/Process.ts | 3 +- browser/src/Plugins/PackageMetadataParser.ts | 4 +- browser/src/Plugins/Plugin.ts | 2 +- .../PluginConfigurationSynchronizer.ts | 2 +- browser/src/Plugins/PluginInstaller.ts | 3 +- browser/src/Redux/LoggingMiddleware.ts | 2 +- browser/src/Services/AutoClosingPairs.ts | 3 +- browser/src/Services/Automation.ts | 3 +- browser/src/Services/CommandManager.ts | 2 +- .../Completion/CompletionsRequestor.ts | 2 +- .../Services/Configuration/Configuration.ts | 2 +- .../Configuration/ConfigurationEditor.ts | 2 +- .../DeprecatedConfigurationValues.ts | 2 +- .../FileConfigurationProvider.ts | 3 +- .../Configuration/UserConfiguration.ts | 4 +- .../src/Services/Explorer/ExplorerStore.ts | 13 +++- .../src/Services/FileSystemWatcher/index.ts | 2 +- browser/src/Services/FocusManager.ts | 2 +- .../Services/IconThemes/IconThemeLoader.ts | 4 +- browser/src/Services/Language/CodeAction.ts | 2 +- .../Services/Language/DefinitionRequestor.ts | 2 +- browser/src/Services/Language/Formatting.ts | 3 +- .../src/Services/Language/HoverRequestor.ts | 3 +- .../src/Services/Language/LanguageClient.ts | 3 +- .../Language/LanguageClientProcess.ts | 3 +- .../Language/LanguageConfiguration.ts | 2 +- .../src/Services/Language/LanguageManager.ts | 3 +- browser/src/Services/Language/PromiseQueue.ts | 2 +- .../src/Services/Language/SignatureHelp.ts | 2 +- browser/src/Services/Notifications/index.ts | 2 +- .../src/Services/QuickOpen/FinderProcess.ts | 2 +- browser/src/Services/Recorder.ts | 2 +- browser/src/Services/Search/index.tsx | 3 +- .../Snippets/SnippetCompletionProvider.ts | 3 +- .../src/Services/Snippets/SnippetManager.ts | 3 +- .../src/Services/Snippets/SnippetProvider.ts | 2 +- .../src/Services/Snippets/SnippetSession.ts | 3 +- .../Services/Snippets/UserSnippetProvider.ts | 3 +- .../SyntaxHighlighting/GrammarLoader.ts | 4 +- .../SyntaxHighlightReconciler.ts | 4 +- .../SyntaxHighlighting/SyntaxHighlighting.ts | 2 +- .../SyntaxHighlightingPeriodicJob.ts | 5 +- .../SyntaxHighlightingStore.ts | 3 +- browser/src/Services/UnhandledErrorMonitor.ts | 2 +- browser/src/Services/Workspace/Workspace.ts | 2 +- .../Workspace/WorkspaceConfiguration.ts | 2 +- browser/src/UI/components/VimNavigator.tsx | 3 +- browser/src/neovim/NeovimInstance.ts | 2 +- browser/src/neovim/NeovimProcessSpawner.ts | 8 ++- .../neovim/NeovimTokenColorSynchronizer.ts | 5 +- browser/src/neovim/NeovimWindowManager.ts | 3 +- browser/src/neovim/Session.ts | 2 +- browser/src/neovim/SharedNeovimInstance.ts | 2 +- package.json | 1 + yarn.lock | 4 ++ 68 files changed, 97 insertions(+), 153 deletions(-) delete mode 100644 browser/src/Log.ts diff --git a/browser/src/App.ts b/browser/src/App.ts index ec225a1046..d1573da854 100644 --- a/browser/src/App.ts +++ b/browser/src/App.ts @@ -11,7 +11,7 @@ import * as path from "path" import { IDisposable } from "oni-types" -import * as Log from "./Log" +import * as Log from "oni-core-logging" import * as Performance from "./Performance" import * as Utility from "./Utility" diff --git a/browser/src/Editor/BufferManager.ts b/browser/src/Editor/BufferManager.ts index 7d1e044817..411447671d 100644 --- a/browser/src/Editor/BufferManager.ts +++ b/browser/src/Editor/BufferManager.ts @@ -18,6 +18,7 @@ import { Store } from "redux" import * as detectIndent from "detect-indent" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { BufferEventContext, @@ -38,7 +39,6 @@ import * as Actions from "./NeovimEditor/NeovimEditorActions" import * as State from "./NeovimEditor/NeovimEditorStore" import * as Constants from "./../Constants" -import * as Log from "./../Log" import { TokenColor } from "./../Services/TokenColors" import { IBufferLayer } from "./NeovimEditor/BufferLayerManager" diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index c42d6df779..52416f8c49 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -20,10 +20,9 @@ import { bindActionCreators, Store } from "redux" import { clipboard, ipcRenderer } from "electron" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" - import { addDefaultUnitIfNeeded } from "./../../Font" import { BufferEventContext, diff --git a/browser/src/Editor/NeovimEditor/Rename.tsx b/browser/src/Editor/NeovimEditor/Rename.tsx index c54350b2b3..c77effc322 100644 --- a/browser/src/Editor/NeovimEditor/Rename.tsx +++ b/browser/src/Editor/NeovimEditor/Rename.tsx @@ -5,8 +5,8 @@ import * as React from "react" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" -import * as Log from "./../../Log" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { LanguageManager } from "./../../Services/Language" diff --git a/browser/src/Editor/NeovimEditor/markdown.ts b/browser/src/Editor/NeovimEditor/markdown.ts index eb41be0de4..a85c6e1384 100644 --- a/browser/src/Editor/NeovimEditor/markdown.ts +++ b/browser/src/Editor/NeovimEditor/markdown.ts @@ -1,7 +1,7 @@ import { unescape } from "lodash" import * as marked from "marked" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { IGrammarPerLine, IGrammarToken } from "./../../Services/SyntaxHighlighting/TokenGenerator" import * as DOMPurify from "dompurify" diff --git a/browser/src/Editor/OniEditor/OniEditor.tsx b/browser/src/Editor/OniEditor/OniEditor.tsx index 0b9e330f92..a3e1e76666 100644 --- a/browser/src/Editor/OniEditor/OniEditor.tsx +++ b/browser/src/Editor/OniEditor/OniEditor.tsx @@ -12,12 +12,12 @@ import * as React from "react" import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { IEvent } from "oni-types" // import { remote } from "electron" import * as App from "./../../App" -import * as Log from "./../../Log" import * as Utility from "./../../Utility" import { PluginManager } from "./../../Plugins/PluginManager" diff --git a/browser/src/Input/Keyboard/KeyboardLayout.ts b/browser/src/Input/Keyboard/KeyboardLayout.ts index 29bbb1f102..2001b5353d 100644 --- a/browser/src/Input/Keyboard/KeyboardLayout.ts +++ b/browser/src/Input/Keyboard/KeyboardLayout.ts @@ -1,6 +1,6 @@ +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" import * as Platform from "./../../Platform" export interface IKeyMap { diff --git a/browser/src/Input/Keyboard/KeyboardResolver.ts b/browser/src/Input/Keyboard/KeyboardResolver.ts index 03853af3b6..c79213d4d8 100644 --- a/browser/src/Input/Keyboard/KeyboardResolver.ts +++ b/browser/src/Input/Keyboard/KeyboardResolver.ts @@ -3,10 +3,9 @@ * * Manages set of resolvers, and adding/removing resolvers. */ +import * as Log from "oni-core-logging" import { IDisposable } from "oni-types" -import * as Log from "./../../Log" - import { KeyResolver } from "./Resolvers" export class KeyboardResolver { diff --git a/browser/src/Log.ts b/browser/src/Log.ts deleted file mode 100644 index e8a34847ca..0000000000 --- a/browser/src/Log.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Log.ts - * - * Utilities for logging in Oni - */ - -// Log levels are the same as `npm`: -// - error -// - warn -// - info -// - verbose -// - debug - -// Debug is not enabled unless explicitly opted in via `enableDebugLogging` (can be executed in the console via `Oni.log.enableDebugLogging()`) -// Verbose is enabled for debug builds, and off for production builds - -let verboseLoggingEnabled = process.env["NODE_ENV"] === "development" // tslint:disable-line no-string-literal -let debugLoggingEnabled = false - -export const debug = (message: string): void => { - if (debugLoggingEnabled) { - console.log(message) // tslint:disable-line no-console - } -} - -export const verbose = (message: string): void => { - if (verboseLoggingEnabled || debugLoggingEnabled) { - console.log(message) // tslint:disable-line no-console - } -} - -export const info = (message: string): void => { - console.log(message) // tslint:disable-line no-console -} - -export const warn = (message: string): void => { - console.warn(message) // tslint:disable-line no-console -} - -export const error = (messageOrError: string | Error, errorDetails?: any): void => { - console.error(messageOrError) // tslint:disable-line no-console -} - -export const isDebugLoggingEnabled = () => debugLoggingEnabled -export const isVerboseLoggingEnabled = () => verboseLoggingEnabled - -export const enableDebugLogging = () => { - debugLoggingEnabled = true -} - -export const disableDebugLogging = () => { - debugLoggingEnabled = false -} - -export const enableVerboseLogging = () => { - verboseLoggingEnabled = true -} - -export const disableVerboseLogging = () => { - verboseLoggingEnabled = false -} diff --git a/browser/src/PeriodicJobs.ts b/browser/src/PeriodicJobs.ts index 76aed5a7a0..04be593554 100644 --- a/browser/src/PeriodicJobs.ts +++ b/browser/src/PeriodicJobs.ts @@ -1,5 +1,5 @@ +import * as Log from "oni-core-logging" import * as Constants from "./Constants" -import * as Log from "./Log" // IPeriodicJob implements the interface for a long-running job // that would be expensive to run synchronously, so it is diff --git a/browser/src/PersistentStore.ts b/browser/src/PersistentStore.ts index 240816478d..22840230e4 100644 --- a/browser/src/PersistentStore.ts +++ b/browser/src/PersistentStore.ts @@ -6,7 +6,7 @@ import { remote } from "electron" -import * as Log from "./Log" +import * as Log from "oni-core-logging" // We need to use the 'main process' version of electron-settings. // See: https://github.com/nathanbuchar/electron-settings/wiki/FAQs diff --git a/browser/src/Plugins/Api/LanguageClient/LanguageClientLogger.ts b/browser/src/Plugins/Api/LanguageClient/LanguageClientLogger.ts index 4546855852..b694270f1f 100644 --- a/browser/src/Plugins/Api/LanguageClient/LanguageClientLogger.ts +++ b/browser/src/Plugins/Api/LanguageClient/LanguageClientLogger.ts @@ -4,7 +4,7 @@ * Helper utility for handling logging from language service clients */ -import * as Log from "./../../../Log" +import * as Log from "oni-core-logging" export class LanguageClientLogger { public error(message: string): void { diff --git a/browser/src/Plugins/Api/Oni.ts b/browser/src/Plugins/Api/Oni.ts index 37e2f63792..db0086c604 100644 --- a/browser/src/Plugins/Api/Oni.ts +++ b/browser/src/Plugins/Api/Oni.ts @@ -8,6 +8,7 @@ import * as ChildProcess from "child_process" import * as OniApi from "oni-api" +import * as Log from "oni-core-logging" import Process from "./Process" import { Services } from "./Services" @@ -38,8 +39,6 @@ import { getInstance as getTokenColorsInstance } from "./../../Services/TokenCol import { windowManager } from "./../../Services/WindowManager" import { getInstance as getWorkspaceInstance } from "./../../Services/Workspace" -import * as Log from "./../../Log" - import * as throttle from "lodash/throttle" const react = require("react") // tslint:disable-line no-var-requires diff --git a/browser/src/Plugins/Api/Process.ts b/browser/src/Plugins/Api/Process.ts index 13ce7a886b..0a099fbdfd 100644 --- a/browser/src/Plugins/Api/Process.ts +++ b/browser/src/Plugins/Api/Process.ts @@ -1,7 +1,8 @@ import * as ChildProcess from "child_process" import * as Oni from "oni-api" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" + import * as Platform from "./../../Platform" import { configuration } from "./../../Services/Configuration" diff --git a/browser/src/Plugins/PackageMetadataParser.ts b/browser/src/Plugins/PackageMetadataParser.ts index af913d8445..ba8153b0d9 100644 --- a/browser/src/Plugins/PackageMetadataParser.ts +++ b/browser/src/Plugins/PackageMetadataParser.ts @@ -7,9 +7,9 @@ import * as fs from "fs" import * as path from "path" -import * as Capabilities from "./Api/Capabilities" +import * as Log from "oni-core-logging" -import * as Log from "./../Log" +import * as Capabilities from "./Api/Capabilities" const remapToAbsolutePaths = ( packageRoot: string, diff --git a/browser/src/Plugins/Plugin.ts b/browser/src/Plugins/Plugin.ts index e1ee4da78c..e56d65f2ff 100644 --- a/browser/src/Plugins/Plugin.ts +++ b/browser/src/Plugins/Plugin.ts @@ -1,7 +1,7 @@ import * as fs from "fs" import * as path from "path" -import * as Log from "./../Log" +import * as Log from "oni-core-logging" import * as Capabilities from "./Api/Capabilities" import { Oni } from "./Api/Oni" diff --git a/browser/src/Plugins/PluginConfigurationSynchronizer.ts b/browser/src/Plugins/PluginConfigurationSynchronizer.ts index 40d4af0e08..6b39ed06f0 100644 --- a/browser/src/Plugins/PluginConfigurationSynchronizer.ts +++ b/browser/src/Plugins/PluginConfigurationSynchronizer.ts @@ -4,7 +4,7 @@ * Responsible for synchronizing user's `plugin` configuration settings. */ -import * as Log from "./../Log" +import * as Log from "oni-core-logging" import { Configuration } from "./../Services/Configuration" import { PluginManager } from "./PluginManager" diff --git a/browser/src/Plugins/PluginInstaller.ts b/browser/src/Plugins/PluginInstaller.ts index d88aa1d7eb..4aa7c7e730 100644 --- a/browser/src/Plugins/PluginInstaller.ts +++ b/browser/src/Plugins/PluginInstaller.ts @@ -6,6 +6,7 @@ import * as path from "path" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" // import * as Oni from "oni-api" @@ -20,8 +21,6 @@ import { IFileSystem, OniFileSystem } from "./../Services/Explorer/ExplorerFileS import Process from "./Api/Process" -import * as Log from "./../Log" - /** * Plugin identifier: * - For _git_, this should be of the form `welle/targets.vim` diff --git a/browser/src/Redux/LoggingMiddleware.ts b/browser/src/Redux/LoggingMiddleware.ts index 60ccd0d974..9e0fae0196 100644 --- a/browser/src/Redux/LoggingMiddleware.ts +++ b/browser/src/Redux/LoggingMiddleware.ts @@ -6,7 +6,7 @@ import { Store } from "redux" -import * as Log from "./../Log" +import * as Log from "oni-core-logging" export const createLoggingMiddleware = (storeName: string) => (store: Store) => ( next: any, diff --git a/browser/src/Services/AutoClosingPairs.ts b/browser/src/Services/AutoClosingPairs.ts index d710239512..314aa65b32 100644 --- a/browser/src/Services/AutoClosingPairs.ts +++ b/browser/src/Services/AutoClosingPairs.ts @@ -5,6 +5,7 @@ */ import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { IBuffer } from "./../Editor/BufferManager" import { Configuration } from "./Configuration" @@ -14,8 +15,6 @@ import { LanguageManager } from "./Language" import { NeovimInstance } from "./../neovim" -import * as Log from "./../Log" - export interface IAutoClosingPair { open: string close: string diff --git a/browser/src/Services/Automation.ts b/browser/src/Services/Automation.ts index 255c0a4633..19600d0d87 100644 --- a/browser/src/Services/Automation.ts +++ b/browser/src/Services/Automation.ts @@ -7,6 +7,7 @@ import { remote } from "electron" import * as OniApi from "oni-api" +import * as Log from "oni-core-logging" import * as App from "./../App" import * as Utility from "./../Utility" @@ -15,8 +16,6 @@ import { getUserConfigFilePath } from "./Configuration" import { editorManager } from "./EditorManager" import { inputManager } from "./InputManager" -import * as Log from "./../Log" - import { IKey, parseKeysFromVimString } from "./../Input/KeyParser" export interface ITestResult { diff --git a/browser/src/Services/CommandManager.ts b/browser/src/Services/CommandManager.ts index ef5b91f40f..29dec21f7e 100644 --- a/browser/src/Services/CommandManager.ts +++ b/browser/src/Services/CommandManager.ts @@ -7,8 +7,8 @@ import * as values from "lodash/values" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" -import * as Log from "./../Log" import { INeovimInstance } from "./../neovim" import { ITask, ITaskProvider } from "./Tasks" diff --git a/browser/src/Services/Completion/CompletionsRequestor.ts b/browser/src/Services/Completion/CompletionsRequestor.ts index d42cb47400..c546b12ab7 100644 --- a/browser/src/Services/Completion/CompletionsRequestor.ts +++ b/browser/src/Services/Completion/CompletionsRequestor.ts @@ -6,7 +6,7 @@ import * as types from "vscode-languageserver-types" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { LanguageManager } from "./../Language" diff --git a/browser/src/Services/Configuration/Configuration.ts b/browser/src/Services/Configuration/Configuration.ts index e6692b511c..a788745331 100644 --- a/browser/src/Services/Configuration/Configuration.ts +++ b/browser/src/Services/Configuration/Configuration.ts @@ -4,9 +4,9 @@ import { merge } from "lodash" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IDisposable, IEvent } from "oni-types" import { applyDefaultKeyBindings } from "./../../Input/KeyBindings" -import * as Log from "./../../Log" import * as Performance from "./../../Performance" import { diff } from "./../../Utility" diff --git a/browser/src/Services/Configuration/ConfigurationEditor.ts b/browser/src/Services/Configuration/ConfigurationEditor.ts index 35aba1bc16..a5155e9fc4 100644 --- a/browser/src/Services/Configuration/ConfigurationEditor.ts +++ b/browser/src/Services/Configuration/ConfigurationEditor.ts @@ -9,7 +9,7 @@ import * as path from "path" import * as mkdirp from "mkdirp" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { EditorManager } from "./../EditorManager" diff --git a/browser/src/Services/Configuration/DeprecatedConfigurationValues.ts b/browser/src/Services/Configuration/DeprecatedConfigurationValues.ts index 841feba670..d56d52f2eb 100644 --- a/browser/src/Services/Configuration/DeprecatedConfigurationValues.ts +++ b/browser/src/Services/Configuration/DeprecatedConfigurationValues.ts @@ -6,7 +6,7 @@ * deprecation policy in place - like we'll support deprecated configurations for x releases. */ -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" export interface IDeprecatedConfigurationInfo { replacementConfigurationName: string diff --git a/browser/src/Services/Configuration/FileConfigurationProvider.ts b/browser/src/Services/Configuration/FileConfigurationProvider.ts index 912d8803ed..091bfd8695 100644 --- a/browser/src/Services/Configuration/FileConfigurationProvider.ts +++ b/browser/src/Services/Configuration/FileConfigurationProvider.ts @@ -13,10 +13,9 @@ import "rxjs/add/operator/debounceTime" import { Subject } from "rxjs/Subject" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" - import { IConfigurationProvider } from "./Configuration" import { IConfigurationValues } from "./IConfigurationValues" diff --git a/browser/src/Services/Configuration/UserConfiguration.ts b/browser/src/Services/Configuration/UserConfiguration.ts index ff1b0c5130..f8ad35ca26 100644 --- a/browser/src/Services/Configuration/UserConfiguration.ts +++ b/browser/src/Services/Configuration/UserConfiguration.ts @@ -6,9 +6,9 @@ import * as path from "path" -import * as Platform from "./../../Platform" +import * as Log from "oni-core-logging" -import * as Log from "./../../Log" +import * as Platform from "./../../Platform" export const getUserConfigFilePath = (): string => { const configFileFromEnv = process.env["ONI_CONFIG_FILE"] as string // tslint:disable-line diff --git a/browser/src/Services/Explorer/ExplorerStore.ts b/browser/src/Services/Explorer/ExplorerStore.ts index e522b86792..e235927aa7 100644 --- a/browser/src/Services/Explorer/ExplorerStore.ts +++ b/browser/src/Services/Explorer/ExplorerStore.ts @@ -16,7 +16,8 @@ import { forkJoin } from "rxjs/observable/forkJoin" import { fromPromise } from "rxjs/observable/fromPromise" import { timer } from "rxjs/observable/timer" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" + import { createStore as createReduxStore } from "./../../Redux" import { configuration } from "./../Configuration" import { EmptyNode, ExplorerNode } from "./ExplorerSelectors" @@ -801,7 +802,9 @@ const persistOrDeleteNode = async ( export const undoEpic: ExplorerEpic = (action$, store, { fileSystem }) => action$.ofType("UNDO").mergeMap(action => { - const { register: { undo } } = store.getState() + const { + register: { undo }, + } = store.getState() const lastAction = last(undo) switch (lastAction.type) { @@ -919,7 +922,11 @@ const expandDirectoryEpic: ExplorerEpic = (action$, store, { fileSystem }) => export const createNodeEpic: ExplorerEpic = (action$, store, { fileSystem }) => action$.ofType("CREATE_NODE_COMMIT").mergeMap(({ name }: ICreateNodeCommitAction) => { - const { register: { create: { nodeType } } } = store.getState() + const { + register: { + create: { nodeType }, + }, + } = store.getState() const shouldExpand = Actions.expandDirectory(path.dirname(name)) const createFileOrFolder = nodeType === "file" ? fileSystem.writeFile(name) : fileSystem.mkdir(name) diff --git a/browser/src/Services/FileSystemWatcher/index.ts b/browser/src/Services/FileSystemWatcher/index.ts index b3210aadd4..ddf967c4dd 100644 --- a/browser/src/Services/FileSystemWatcher/index.ts +++ b/browser/src/Services/FileSystemWatcher/index.ts @@ -2,7 +2,7 @@ import * as chokidar from "chokidar" import { Stats } from "fs" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" export type Targets = string | string[] diff --git a/browser/src/Services/FocusManager.ts b/browser/src/Services/FocusManager.ts index 9671789883..3a9a90eb83 100644 --- a/browser/src/Services/FocusManager.ts +++ b/browser/src/Services/FocusManager.ts @@ -2,7 +2,7 @@ * FocusManager.ts */ -import * as Log from "./../Log" +import * as Log from "oni-core-logging" class FocusManager { private _focusElementStack: HTMLElement[] = [] diff --git a/browser/src/Services/IconThemes/IconThemeLoader.ts b/browser/src/Services/IconThemes/IconThemeLoader.ts index fb58b5bc22..9e675e4602 100644 --- a/browser/src/Services/IconThemes/IconThemeLoader.ts +++ b/browser/src/Services/IconThemes/IconThemeLoader.ts @@ -5,11 +5,13 @@ */ import * as fs from "fs" + +import * as Log from "oni-core-logging" + import { IIconThemeContribution } from "./../../Plugins/Api/Capabilities" import { IIconTheme } from "./Icons" -import * as Log from "./../../Log" import { PluginManager } from "./../../Plugins/PluginManager" export interface IIconThemeLoadResult { diff --git a/browser/src/Services/Language/CodeAction.ts b/browser/src/Services/Language/CodeAction.ts index 7be0e921e6..5baa8fb50f 100644 --- a/browser/src/Services/Language/CodeAction.ts +++ b/browser/src/Services/Language/CodeAction.ts @@ -6,6 +6,7 @@ // import * as os from "os" import * as types from "vscode-languageserver-types" +import * as Log from "oni-core-logging" // import { configuration } from "./../Configuration" // import * as UI from "./../../UI" @@ -13,7 +14,6 @@ import * as types from "vscode-languageserver-types" // import { contextMenuManager } from "./../ContextMenu" import * as LanguageManager from "./LanguageManager" -import * as Log from "./../../Log" import { editorManager } from "./../EditorManager" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" diff --git a/browser/src/Services/Language/DefinitionRequestor.ts b/browser/src/Services/Language/DefinitionRequestor.ts index 093e08fc90..80ca308403 100644 --- a/browser/src/Services/Language/DefinitionRequestor.ts +++ b/browser/src/Services/Language/DefinitionRequestor.ts @@ -7,8 +7,8 @@ import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" -import * as Log from "./../../Log" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { LanguageManager } from "./LanguageManager" diff --git a/browser/src/Services/Language/Formatting.ts b/browser/src/Services/Language/Formatting.ts index c9b13e049b..c074ce9100 100644 --- a/browser/src/Services/Language/Formatting.ts +++ b/browser/src/Services/Language/Formatting.ts @@ -4,7 +4,8 @@ import * as types from "vscode-languageserver-types" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" + import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { editorManager } from "./../EditorManager" diff --git a/browser/src/Services/Language/HoverRequestor.ts b/browser/src/Services/Language/HoverRequestor.ts index 234f358312..f06cfc2ba8 100644 --- a/browser/src/Services/Language/HoverRequestor.ts +++ b/browser/src/Services/Language/HoverRequestor.ts @@ -6,7 +6,8 @@ import * as types from "vscode-languageserver-types" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" + import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { LanguageManager } from "./LanguageManager" diff --git a/browser/src/Services/Language/LanguageClient.ts b/browser/src/Services/Language/LanguageClient.ts index 74a2ea702b..1c495a8352 100644 --- a/browser/src/Services/Language/LanguageClient.ts +++ b/browser/src/Services/Language/LanguageClient.ts @@ -1,9 +1,8 @@ import * as rpc from "vscode-jsonrpc" +import * as Log from "oni-core-logging" import { Event } from "oni-types" -import * as Log from "./../../Log" - import { ILanguageClientProcess } from "./LanguageClientProcess" import { PromiseQueue } from "./PromiseQueue" import { IServerCapabilities } from "./ServerCapabilities" diff --git a/browser/src/Services/Language/LanguageClientProcess.ts b/browser/src/Services/Language/LanguageClientProcess.ts index 0969119fa1..43cd70747f 100644 --- a/browser/src/Services/Language/LanguageClientProcess.ts +++ b/browser/src/Services/Language/LanguageClientProcess.ts @@ -14,10 +14,9 @@ import * as path from "path" import { ChildProcess } from "child_process" import * as rpc from "vscode-jsonrpc" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" - import { normalizePath } from "./../../Utility" import { LanguageClientLogger } from "./../../Plugins/Api/LanguageClient/LanguageClientLogger" diff --git a/browser/src/Services/Language/LanguageConfiguration.ts b/browser/src/Services/Language/LanguageConfiguration.ts index 46c29cad44..624fa7a9ff 100644 --- a/browser/src/Services/Language/LanguageConfiguration.ts +++ b/browser/src/Services/Language/LanguageConfiguration.ts @@ -4,7 +4,7 @@ * Helper for registering language client information from config */ -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { LanguageClient } from "./LanguageClient" import { diff --git a/browser/src/Services/Language/LanguageManager.ts b/browser/src/Services/Language/LanguageManager.ts index 315d07ec10..38b13bb752 100644 --- a/browser/src/Services/Language/LanguageManager.ts +++ b/browser/src/Services/Language/LanguageManager.ts @@ -11,10 +11,9 @@ import * as os from "os" import * as path from "path" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IDisposable } from "oni-types" -import * as Log from "./../../Log" - import { ILanguageClient } from "./LanguageClient" import * as LanguageClientTypes from "./LanguageClientTypes" import { IServerCapabilities } from "./ServerCapabilities" diff --git a/browser/src/Services/Language/PromiseQueue.ts b/browser/src/Services/Language/PromiseQueue.ts index 87df4f9c54..40d7a34dad 100644 --- a/browser/src/Services/Language/PromiseQueue.ts +++ b/browser/src/Services/Language/PromiseQueue.ts @@ -1,4 +1,4 @@ -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" export class PromiseQueue { private _currentPromise: Promise = Promise.resolve(null) diff --git a/browser/src/Services/Language/SignatureHelp.ts b/browser/src/Services/Language/SignatureHelp.ts index feb956f320..92c291b9b5 100644 --- a/browser/src/Services/Language/SignatureHelp.ts +++ b/browser/src/Services/Language/SignatureHelp.ts @@ -7,8 +7,8 @@ import { Observable } from "rxjs/Observable" import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" -import * as Log from "./../../Log" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { IToolTipsProvider } from "./../../Editor/NeovimEditor/ToolTipsProvider" diff --git a/browser/src/Services/Notifications/index.ts b/browser/src/Services/Notifications/index.ts index e6b769ebae..96bdf54b43 100644 --- a/browser/src/Services/Notifications/index.ts +++ b/browser/src/Services/Notifications/index.ts @@ -2,7 +2,7 @@ * index.ts */ -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { OverlayManager } from "./../Overlay" diff --git a/browser/src/Services/QuickOpen/FinderProcess.ts b/browser/src/Services/QuickOpen/FinderProcess.ts index 250853cc1d..104ff26546 100644 --- a/browser/src/Services/QuickOpen/FinderProcess.ts +++ b/browser/src/Services/QuickOpen/FinderProcess.ts @@ -8,7 +8,7 @@ import { ChildProcess, spawn } from "child_process" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { getInstance } from "./../Workspace" export class FinderProcess { diff --git a/browser/src/Services/Recorder.ts b/browser/src/Services/Recorder.ts index 9632fb2078..390b912066 100644 --- a/browser/src/Services/Recorder.ts +++ b/browser/src/Services/Recorder.ts @@ -12,7 +12,7 @@ import * as path from "path" import * as Oni from "oni-api" -import * as Log from "./../Log" +import * as Log from "oni-core-logging" import { configuration } from "./Configuration" import { getInstance as getNotificationsInstance } from "./Notifications" diff --git a/browser/src/Services/Search/index.tsx b/browser/src/Services/Search/index.tsx index 465b56061a..f6227edb87 100644 --- a/browser/src/Services/Search/index.tsx +++ b/browser/src/Services/Search/index.tsx @@ -8,6 +8,7 @@ import * as React from "react" import { Subject } from "rxjs/Subject" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" import { CommandManager } from "./../CommandManager" @@ -15,8 +16,6 @@ import { EditorManager } from "./../EditorManager" import { SidebarManager } from "./../Sidebar" import { Workspace } from "./../Workspace" -import * as Log from "./../../Log" - export * from "./SearchProvider" import { diff --git a/browser/src/Services/Snippets/SnippetCompletionProvider.ts b/browser/src/Services/Snippets/SnippetCompletionProvider.ts index 08c6a0583d..b8762c70c0 100644 --- a/browser/src/Services/Snippets/SnippetCompletionProvider.ts +++ b/browser/src/Services/Snippets/SnippetCompletionProvider.ts @@ -5,11 +5,10 @@ */ import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import * as types from "vscode-languageserver-types" -import * as Log from "./../../Log" - import { CompletionsRequestContext, ICompletionsRequestor } from "./../Completion" import { SnippetManager } from "./SnippetManager" diff --git a/browser/src/Services/Snippets/SnippetManager.ts b/browser/src/Services/Snippets/SnippetManager.ts index 73f46b3623..c33ab5786b 100644 --- a/browser/src/Services/Snippets/SnippetManager.ts +++ b/browser/src/Services/Snippets/SnippetManager.ts @@ -5,10 +5,9 @@ */ import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { IDisposable } from "oni-types" -import * as Log from "./../../Log" - import "rxjs/add/operator/auditTime" import { Subject } from "rxjs/Subject" diff --git a/browser/src/Services/Snippets/SnippetProvider.ts b/browser/src/Services/Snippets/SnippetProvider.ts index ae57baa54e..18ec7947b2 100644 --- a/browser/src/Services/Snippets/SnippetProvider.ts +++ b/browser/src/Services/Snippets/SnippetProvider.ts @@ -8,12 +8,12 @@ import * as fs from "fs" import * as os from "os" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { PluginManager } from "./../../Plugins/PluginManager" import { Configuration } from "./../Configuration" -import * as Log from "./../../Log" import * as Utility from "./../../Utility" export class CompositeSnippetProvider implements Oni.Snippets.SnippetProvider { diff --git a/browser/src/Services/Snippets/SnippetSession.ts b/browser/src/Services/Snippets/SnippetSession.ts index 5118ad64cd..e1202a1bd5 100644 --- a/browser/src/Services/Snippets/SnippetSession.ts +++ b/browser/src/Services/Snippets/SnippetSession.ts @@ -7,10 +7,9 @@ import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" - import { OniSnippet, OniSnippetPlaceholder } from "./OniSnippet" import { BufferIndentationInfo, IBuffer } from "./../../Editor/BufferManager" diff --git a/browser/src/Services/Snippets/UserSnippetProvider.ts b/browser/src/Services/Snippets/UserSnippetProvider.ts index 46127227e3..06d7cc6df6 100644 --- a/browser/src/Services/Snippets/UserSnippetProvider.ts +++ b/browser/src/Services/Snippets/UserSnippetProvider.ts @@ -9,6 +9,7 @@ import * as path from "path" import * as mkdirp from "mkdirp" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { loadSnippetsFromFile } from "./SnippetProvider" @@ -16,8 +17,6 @@ import { CommandManager } from "./../CommandManager" import { Configuration, getUserConfigFolderPath } from "./../Configuration" import { EditorManager } from "./../EditorManager" -import * as Log from "./../../Log" - const GLOBAL_SNIPPET_NAME = "global_snippets" const SnippetTemplate = [ diff --git a/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts b/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts index 612ed200fa..0e90a623e4 100644 --- a/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts +++ b/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts @@ -1,8 +1,8 @@ import { IGrammar, Registry } from "vscode-textmate" -import { configuration } from "./../Configuration" +import * as Log from "oni-core-logging" -import * as Log from "./../../Log" +import { configuration } from "./../Configuration" export interface IGrammarLoader { getGrammarForLanguage(language: string, extension: string): Promise diff --git a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightReconciler.ts b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightReconciler.ts index 730afd010a..0c25b9cfba 100644 --- a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightReconciler.ts +++ b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightReconciler.ts @@ -4,6 +4,8 @@ * Handles enhanced syntax highlighting */ +import * as Log from "oni-core-logging" + import { TokenColor, TokenColors } from "./../TokenColors" import { NeovimEditor } from "./../../Editor/NeovimEditor" @@ -17,8 +19,6 @@ import { import * as Selectors from "./SyntaxHighlightSelectors" -import * as Log from "./../../Log" - // SyntaxHighlightReconciler // // Essentially a renderer / reconciler, that will push diff --git a/browser/src/Services/SyntaxHighlighting/SyntaxHighlighting.ts b/browser/src/Services/SyntaxHighlighting/SyntaxHighlighting.ts index dbac0edd83..ff3afec237 100644 --- a/browser/src/Services/SyntaxHighlighting/SyntaxHighlighting.ts +++ b/browser/src/Services/SyntaxHighlighting/SyntaxHighlighting.ts @@ -12,6 +12,7 @@ import { Subject } from "rxjs/Subject" import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Store, Unsubscribe } from "redux" @@ -30,7 +31,6 @@ import { ISyntaxHighlighter } from "./ISyntaxHighlighter" import { SyntaxHighlightReconciler } from "./SyntaxHighlightReconciler" import { getLineFromBuffer } from "./SyntaxHighlightSelectors" -import * as Log from "./../../Log" import * as Utility from "./../../Utility" export class SyntaxHighlighter implements ISyntaxHighlighter { diff --git a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingPeriodicJob.ts b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingPeriodicJob.ts index c509ba57b5..dbb1093bee 100644 --- a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingPeriodicJob.ts +++ b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingPeriodicJob.ts @@ -7,16 +7,15 @@ import { Store } from "redux" import * as types from "vscode-languageserver-types" - import { IGrammar } from "vscode-textmate" +import * as Log from "oni-core-logging" + import * as SyntaxHighlighting from "./SyntaxHighlightingStore" import * as Selectors from "./SyntaxHighlightSelectors" import { IPeriodicJob } from "./../../PeriodicJobs" -import * as Log from "./../../Log" - export const SYNTAX_JOB_BUDGET = 10 // Budget in milliseconds - time to allow the job to run for export class SyntaxHighlightingPeriodicJob implements IPeriodicJob { diff --git a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingStore.ts b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingStore.ts index 7eba48f44a..c22ea3a083 100644 --- a/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingStore.ts +++ b/browser/src/Services/SyntaxHighlighting/SyntaxHighlightingStore.ts @@ -8,7 +8,8 @@ import { Store } from "redux" import * as types from "vscode-languageserver-types" import { StackElement } from "vscode-textmate" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" + import * as PeriodicJobs from "./../../PeriodicJobs" import { createStore } from "./../../Redux" import { configuration } from "./../Configuration" diff --git a/browser/src/Services/UnhandledErrorMonitor.ts b/browser/src/Services/UnhandledErrorMonitor.ts index 6b00c530a7..c4824ff2fa 100644 --- a/browser/src/Services/UnhandledErrorMonitor.ts +++ b/browser/src/Services/UnhandledErrorMonitor.ts @@ -9,7 +9,7 @@ import { Event, IEvent } from "oni-types" import { Configuration } from "./Configuration" import { Notifications } from "./Notifications" -import * as Log from "./../Log" +import * as Log from "oni-core-logging" export class UnhandledErrorMonitor { private _onUnhandledErrorEvent = new Event() diff --git a/browser/src/Services/Workspace/Workspace.ts b/browser/src/Services/Workspace/Workspace.ts index 8d33243fad..c87764520f 100644 --- a/browser/src/Services/Workspace/Workspace.ts +++ b/browser/src/Services/Workspace/Workspace.ts @@ -19,9 +19,9 @@ import { Observable } from "rxjs/Observable" import * as types from "vscode-languageserver-types" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" -import * as Log from "./../../Log" import * as Helpers from "./../../Plugins/Api/LanguageClient/LanguageClientHelpers" import { Configuration } from "./../Configuration" diff --git a/browser/src/Services/Workspace/WorkspaceConfiguration.ts b/browser/src/Services/Workspace/WorkspaceConfiguration.ts index 0e09ad6dbf..1a834dc63a 100644 --- a/browser/src/Services/Workspace/WorkspaceConfiguration.ts +++ b/browser/src/Services/Workspace/WorkspaceConfiguration.ts @@ -7,7 +7,7 @@ import * as fs from "fs" import * as path from "path" -import * as Log from "./../../Log" +import * as Log from "oni-core-logging" import { Configuration } from "./../Configuration" import { IWorkspace } from "./Workspace" diff --git a/browser/src/UI/components/VimNavigator.tsx b/browser/src/UI/components/VimNavigator.tsx index 7cc3d17409..b4711f9243 100644 --- a/browser/src/UI/components/VimNavigator.tsx +++ b/browser/src/UI/components/VimNavigator.tsx @@ -12,6 +12,7 @@ import * as React from "react" +import * as Log from "oni-core-logging" import { Event } from "oni-types" import { KeyboardInputView } from "./../../Input/KeyboardInput" @@ -19,8 +20,6 @@ import { getInstance, IMenuBinding } from "./../../neovim/SharedNeovimInstance" import { CallbackCommand, commandManager } from "./../../Services/CommandManager" -import * as Log from "./../../Log" - export interface IVimNavigatorProps { // activateOnMount: boolean ids: string[] diff --git a/browser/src/neovim/NeovimInstance.ts b/browser/src/neovim/NeovimInstance.ts index 9df1a3c282..6f4c9dbaf6 100644 --- a/browser/src/neovim/NeovimInstance.ts +++ b/browser/src/neovim/NeovimInstance.ts @@ -4,9 +4,9 @@ import * as path from "path" import * as mkdirp from "mkdirp" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IDisposable, IEvent } from "oni-types" -import * as Log from "./../Log" import * as Performance from "./../Performance" import { CommandContext } from "./CommandContext" import { EventContext } from "./EventContext" diff --git a/browser/src/neovim/NeovimProcessSpawner.ts b/browser/src/neovim/NeovimProcessSpawner.ts index 580c7b0a8c..ff5808a82f 100644 --- a/browser/src/neovim/NeovimProcessSpawner.ts +++ b/browser/src/neovim/NeovimProcessSpawner.ts @@ -2,13 +2,13 @@ import { ChildProcess } from "child_process" import * as net from "net" import * as path from "path" +import * as Log from "oni-core-logging" + import * as Platform from "./../Platform" import Process from "./../Plugins/Api/Process" import { Session } from "./Session" -import * as Log from "./../Log" - // Most of the paths coming in the packaged binary reference the `app.asar`, // but the binaries (Neovim) as well as the .vim files are unpacked, // so these need to be mapped to the `app.asar.unpacked` directory @@ -93,7 +93,9 @@ export const startNeovim = async ( let nvimProcessPath = Platform.isWindows() ? nvimWindowsProcessPath - : Platform.isMac() ? nvimMacProcessPath : nvimLinuxPath + : Platform.isMac() + ? nvimMacProcessPath + : nvimLinuxPath nvimProcessPath = remapPathToUnpackedAsar(nvimProcessPath) diff --git a/browser/src/neovim/NeovimTokenColorSynchronizer.ts b/browser/src/neovim/NeovimTokenColorSynchronizer.ts index aea1f29e1e..0d2b82af91 100644 --- a/browser/src/neovim/NeovimTokenColorSynchronizer.ts +++ b/browser/src/neovim/NeovimTokenColorSynchronizer.ts @@ -6,12 +6,13 @@ */ import * as Color from "color" + +import * as Log from "oni-core-logging" + import { TokenColor } from "./../Services/TokenColors" import { NeovimInstance } from "./NeovimInstance" -import * as Log from "./../Log" - const getGuiStringFromTokenColor = (color: TokenColor): string => { if (color.settings.bold && color.settings.italic) { return "gui=bold,italic" diff --git a/browser/src/neovim/NeovimWindowManager.ts b/browser/src/neovim/NeovimWindowManager.ts index 2aefac48ee..d59f89a1fd 100644 --- a/browser/src/neovim/NeovimWindowManager.ts +++ b/browser/src/neovim/NeovimWindowManager.ts @@ -13,13 +13,14 @@ import "rxjs/add/operator/distinctUntilChanged" import * as isEqual from "lodash/isEqual" import * as Oni from "oni-api" +import * as Log from "oni-core-logging" import { Event, IEvent } from "oni-types" + import * as types from "vscode-languageserver-types" import { EventContext } from "./EventContext" import { NeovimInstance } from "./index" -import * as Log from "./../Log" import * as Utility from "./../Utility" export interface NeovimTabPageState { diff --git a/browser/src/neovim/Session.ts b/browser/src/neovim/Session.ts index 5e7760c161..5d4452fcf3 100644 --- a/browser/src/neovim/Session.ts +++ b/browser/src/neovim/Session.ts @@ -2,7 +2,7 @@ import * as msgpackLite from "msgpack-lite" import { EventEmitter } from "events" -import * as Log from "./../Log" +import * as Log from "oni-core-logging" import { configuration } from "./../Services/Configuration" diff --git a/browser/src/neovim/SharedNeovimInstance.ts b/browser/src/neovim/SharedNeovimInstance.ts index 290522fec9..a18cb3f5fa 100644 --- a/browser/src/neovim/SharedNeovimInstance.ts +++ b/browser/src/neovim/SharedNeovimInstance.ts @@ -8,6 +8,7 @@ * - Enabling Neovim keybindings in text input elements */ +import * as Log from "oni-core-logging" import { Event, IDisposable, IEvent } from "oni-types" import { CommandContext } from "./CommandContext" @@ -21,7 +22,6 @@ import { Configuration } from "./../Services/Configuration" import { PromiseQueue } from "./../Services/Language/PromiseQueue" import * as App from "./../App" -import * as Log from "./../Log" export interface IBinding { input(key: string): Promise diff --git a/package.json b/package.json index 3518483696..e88f8aaa5c 100644 --- a/package.json +++ b/package.json @@ -958,6 +958,7 @@ "mocha": "3.1.2", "node-abi": "^2.4.1", "nyc": "^11.4.1", + "oni-core-logging": "^1.0.0", "oni-release-downloader": "^0.0.10", "opencollective": "1.0.3", "prettier": "^1.12.1", diff --git a/yarn.lock b/yarn.lock index 3f77e84f6d..5029183876 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7337,6 +7337,10 @@ oni-api@^0.0.45: version "0.0.45" resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.45.tgz#e9191fbc5069e01b3cbea644bc697be9aaf1d699" +oni-core-logging@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oni-core-logging/-/oni-core-logging-1.0.0.tgz#7ad6c0ad8b06c23255202f97e229c2b0947dcf0b" + oni-neovim-binaries@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/oni-neovim-binaries/-/oni-neovim-binaries-0.1.1.tgz#7aed74c14bca2581e1447c557541192dd5e89cdd" From 3a74bd8ada45ff9ba032c80beb600c0bfdc5b990 Mon Sep 17 00:00:00 2001 From: Ryan C Date: Tue, 5 Jun 2018 15:32:50 +0100 Subject: [PATCH 079/102] Update all submodules. (#2287) --- vim/core/typescript-vim | 2 +- vim/default/bundle/targets.vim | 2 +- vim/default/bundle/vim-commentary | 2 +- vim/default/bundle/vim-surround | 2 +- vim/default/bundle/vim-unimpaired | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vim/core/typescript-vim b/vim/core/typescript-vim index fbd0e9e508..e25636b442 160000 --- a/vim/core/typescript-vim +++ b/vim/core/typescript-vim @@ -1 +1 @@ -Subproject commit fbd0e9e508535f7d89778f7280dc76505348dcc6 +Subproject commit e25636b44211a4be7b089bfed7cf09aa7dd086f5 diff --git a/vim/default/bundle/targets.vim b/vim/default/bundle/targets.vim index dec6409fb8..c3042dc18a 160000 --- a/vim/default/bundle/targets.vim +++ b/vim/default/bundle/targets.vim @@ -1 +1 @@ -Subproject commit dec6409fb80ec1554bad582652f7511aa28c4ed2 +Subproject commit c3042dc18acc0dfcee479310d3efc6aefe92db75 diff --git a/vim/default/bundle/vim-commentary b/vim/default/bundle/vim-commentary index 89f43af186..7f2127b1df 160000 --- a/vim/default/bundle/vim-commentary +++ b/vim/default/bundle/vim-commentary @@ -1 +1 @@ -Subproject commit 89f43af18692d22ed999c3097e449f12fdd8b299 +Subproject commit 7f2127b1dfc57811112785985b46ff2289d72334 diff --git a/vim/default/bundle/vim-surround b/vim/default/bundle/vim-surround index e49d6c2459..643a42454b 160000 --- a/vim/default/bundle/vim-surround +++ b/vim/default/bundle/vim-surround @@ -1 +1 @@ -Subproject commit e49d6c2459e0f5569ff2d533b4df995dd7f98313 +Subproject commit 643a42454bc8c2b2735de14f309523ce733a5358 diff --git a/vim/default/bundle/vim-unimpaired b/vim/default/bundle/vim-unimpaired index 3a7759075c..4afeced83c 160000 --- a/vim/default/bundle/vim-unimpaired +++ b/vim/default/bundle/vim-unimpaired @@ -1 +1 @@ -Subproject commit 3a7759075cca5b0dc29ce81f2747489b6c8e36a7 +Subproject commit 4afeced83cb6bf32ba508fd57ad7dc45c96478f3 From 42840530d89f498f5bd55046573eaa448dfc16d5 Mon Sep 17 00:00:00 2001 From: Akin Date: Tue, 5 Jun 2018 18:30:55 +0100 Subject: [PATCH 080/102] Bugfix/ Fix staggered cursor positioner rendering (#2285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes what is arguably a small but potentially (at least for me *incredibly frustrating*) bug which is that when the hover renderer appears depending on where it is it seems to shuffle into place aka renders in one position then repositions v. quickly before settling into place 💢. Hopefully this PR helps resolve this frustration for anyone else who is as finicky as me re. such things. Before: ![broken_hover](https://user-images.githubusercontent.com/22454918/40940777-c79b1810-6840-11e8-9754-61c228159e36.gif) After: ![fixed_hover](https://user-images.githubusercontent.com/22454918/40940791-ce466552-6840-11e8-8c7f-7be573aa3fcc.gif) --- .../src/UI/components/CursorPositioner.tsx | 182 ++++++++++-------- 1 file changed, 100 insertions(+), 82 deletions(-) diff --git a/browser/src/UI/components/CursorPositioner.tsx b/browser/src/UI/components/CursorPositioner.tsx index 3adb1128c5..4d35f0b9c4 100644 --- a/browser/src/UI/components/CursorPositioner.tsx +++ b/browser/src/UI/components/CursorPositioner.tsx @@ -13,6 +13,7 @@ import * as Oni from "oni-api" import { IState } from "./../../Editor/NeovimEditor/NeovimEditorStore" import { Arrow, ArrowDirection } from "./Arrow" +import styled, { pixel, withProps } from "./common" export enum OpenDirection { Up = 1, @@ -40,31 +41,77 @@ export interface ICursorPositionerViewProps extends ICursorPositionerProps { backgroundColor: string } -export interface ICursorPositionerViewState { +interface ContainerProps { + adjustedY: number + containerWidth: number isMeasured: boolean +} +interface ArrowContainerProps { + x: number + fontPixelWidth: number + hideArrow: boolean + shouldOpenDownwards: boolean +} + +interface ChildProps { + adjustedX: number + isFullWidth: boolean + shouldOpenDownwards: boolean +} + +type VisibilityProperty = "hidden" | "visible" + +const PositionerContainer = withProps(styled.div).attrs({ + style: ({ adjustedY, containerWidth, isMeasured }: ContainerProps) => ({ + top: pixel(adjustedY), + width: pixel(containerWidth), + // Wait until we've measured the bounds to show.. + visibility: (isMeasured ? "visible" : "hidden") as VisibilityProperty, + }), +})` + position: absolute; + left: 0px; + max-width: 45vw; +` + +const openFromBottomStyle = { bottom: "0px" } +const openFromTopStyle = { top: "0px" } + +const PositionerChild = withProps(styled.div).attrs({ + style: (props: ChildProps) => ({ + ...(props.shouldOpenDownwards ? openFromTopStyle : openFromBottomStyle), + left: props.isFullWidth ? "8px" : pixel(Math.abs(props.adjustedX)), + right: props.isFullWidth ? "8px" : null, + }), +})` + position: absolute; + width: fit-content; +` + +const ArrowContainer = withProps(styled.div).attrs({ + style: (props: ArrowContainerProps) => ({ + ...(props.shouldOpenDownwards ? openFromBottomStyle : openFromTopStyle), + left: pixel(props.x + props.fontPixelWidth / 2), + visibility: (props.hideArrow ? "hidden" : "visible") as VisibilityProperty, + }), +})` + position: absolute; + width: fit-content; +` + +export interface ICursorPositionerViewState { + isMeasured: boolean isFullWidth: boolean shouldOpenDownward: boolean adjustedX: number + adjustedY: number lastMeasuredX: number lastMeasuredY: number lastMeasuredHeight: number lastMeasuredWidth: number } -const InitialState = { - isMeasured: false, - - isFullWidth: false, - shouldOpenDownward: false, - adjustedX: 0, - - lastMeasuredX: -1, - lastMeasuredY: -1, - lastMeasuredHeight: 0, - lastMeasuredWidth: 0, -} - /** * Helper component to position an element relative to the current cursor position */ @@ -72,15 +119,20 @@ export class CursorPositionerView extends React.PureComponent< ICursorPositionerViewProps, ICursorPositionerViewState > { + public state = { + isMeasured: false, + isFullWidth: false, + shouldOpenDownward: false, + adjustedX: 0, + adjustedY: 0, + lastMeasuredX: -1, + lastMeasuredY: -1, + lastMeasuredHeight: 0, + lastMeasuredWidth: 0, + } + private _element: HTMLElement private _resizeObserver: any - private _timeout: any - - constructor(props: ICursorPositionerViewProps) { - super(props) - - this.state = InitialState - } public componentDidMount(): void { if (this._element) { @@ -101,14 +153,7 @@ export class CursorPositionerView extends React.PureComponent< return } - if (this._timeout) { - window.clearTimeout(this._timeout) - } - - this._timeout = window.setTimeout(() => { - this._measureElement(this._element) - this._timeout = null - }, 80) + this._measureElement(this._element) }) this._resizeObserver.observe(this._element) @@ -123,64 +168,37 @@ export class CursorPositionerView extends React.PureComponent< } public render(): JSX.Element { - const adjustedX = this.state.adjustedX const adjustedY = this.state.shouldOpenDownward ? this.props.y + this.props.lineHeight * 2.5 : this.props.y - const containerStyle: React.CSSProperties = { - position: "absolute", - top: adjustedY.toString() + "px", - left: "0px", - width: this.props.containerWidth.toString() + "px", - maxWidth: "45vw", - visibility: this.state.isMeasured ? "visible" : "hidden", // Wait until we've measured the bounds to show.. - } - - const openFromBottomStyle: React.CSSProperties = { - position: "absolute", - bottom: "0px", - width: "fit-content", - } - - const openFromTopStyle: React.CSSProperties = { - position: "absolute", - top: "0px", - width: "fit-content", - } - - const childStyle = this.state.shouldOpenDownward ? openFromTopStyle : openFromBottomStyle - const arrowStyle = this.state.shouldOpenDownward ? openFromBottomStyle : openFromTopStyle - - const arrowStyleWithAdjustments: React.CSSProperties = { - ...arrowStyle, - left: (this.props.x + this.props.fontPixelWidth / 2).toString() + "px", - visibility: this.props.hideArrow ? "hidden" : "visible", - } - - const childStyleWithAdjustments: React.CSSProperties = this.state.isMeasured - ? { - ...childStyle, - left: this.state.isFullWidth ? "8px" : Math.abs(adjustedX).toString() + "px", - right: this.state.isFullWidth ? "8px" : null, - } - : childStyle + const arrowDirection = this.state.shouldOpenDownward + ? ArrowDirection.Up + : ArrowDirection.Down return ( -
    -
    + +
    (this._element = elem)}>{this.props.children}
    -
    -
    - -
    -
    + + + + + ) } @@ -200,13 +218,13 @@ export class CursorPositionerView extends React.PureComponent< const margin = this.props.lineHeight * 2 const canOpenUpward = this.props.y - rect.height > margin const bottomScreenPadding = 50 - const canOpenDownard = + const canOpenDownwards = this.props.y + rect.height + this.props.lineHeight * 3 < this.props.containerHeight - margin - bottomScreenPadding const shouldOpenDownward = (this.props.openDirection !== OpenDirection.Down && !canOpenUpward) || - (this.props.openDirection === OpenDirection.Down && canOpenDownard) + (this.props.openDirection === OpenDirection.Down && canOpenDownwards) const rightBounds = this.props.x + rect.width From 68d1f1f816dab516709626499ab62f39fb523135 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Fri, 8 Jun 2018 02:37:05 +0000 Subject: [PATCH 081/102] Allow explorer root to toggle (#2290) --- .../Services/Explorer/ExplorerSelectors.ts | 2 +- .../src/Services/Explorer/ExplorerSplit.tsx | 28 ++++++++------ .../Explorer/ExplorerSelectorsTests.ts | 37 +++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/browser/src/Services/Explorer/ExplorerSelectors.ts b/browser/src/Services/Explorer/ExplorerSelectors.ts index 7aef5f63e8..12f549c43b 100644 --- a/browser/src/Services/Explorer/ExplorerSelectors.ts +++ b/browser/src/Services/Explorer/ExplorerSelectors.ts @@ -80,7 +80,7 @@ export const mapStateToNodeList = (state: IExplorerState): ExplorerNode[] => { ret.push({ id: "explorer", type: "container", - expanded: true, + expanded: !!state.expandedFolders[state.rootFolder.fullPath], name: state.rootFolder.fullPath, }) diff --git a/browser/src/Services/Explorer/ExplorerSplit.tsx b/browser/src/Services/Explorer/ExplorerSplit.tsx index 126e11f088..d2810cb321 100644 --- a/browser/src/Services/Explorer/ExplorerSplit.tsx +++ b/browser/src/Services/Explorer/ExplorerSplit.tsx @@ -112,7 +112,9 @@ export class ExplorerSplit { } private _inputInProgress = () => { - const { register: { rename, create } } = this._store.getState() + const { + register: { rename, create }, + } = this._store.getState() return rename.active || create.active } @@ -239,18 +241,16 @@ export class ExplorerSplit { // Should be being called with an ID not an active editor windowManager.focusSplit("oni.window.0") return + case "container": case "folder": - const isDirectoryExpanded = ExplorerSelectors.isPathExpanded( - state, - selectedItem.folderPath, - ) + const directoryPath = + selectedItem.type === "container" ? selectedItem.name : selectedItem.folderPath + const isDirectoryExpanded = ExplorerSelectors.isPathExpanded(state, directoryPath) this._store.dispatch({ type: isDirectoryExpanded ? "COLLAPSE_DIRECTORY" : "EXPAND_DIRECTORY", - directoryPath: selectedItem.folderPath, + directoryPath, }) return - default: - alert("Not implemented yet.") // tslint:disable-line } } @@ -337,7 +337,9 @@ export class ExplorerSplit { } private _onUndoItem(): void { - const { register: { undo } } = this._store.getState() + const { + register: { undo }, + } = this._store.getState() if (undo.length) { this._store.dispatch({ type: "UNDO" }) } @@ -349,7 +351,9 @@ export class ExplorerSplit { return } - const { register: { yank } } = this._store.getState() + const { + register: { yank }, + } = this._store.getState() const inYankRegister = yank.some(({ id }) => id === selectedItem.id) if (!inYankRegister) { @@ -365,7 +369,9 @@ export class ExplorerSplit { return } - const { register: { yank } } = this._store.getState() + const { + register: { yank }, + } = this._store.getState() if (yank.length && pasteTarget) { const sources = yank.map( diff --git a/browser/test/Services/Explorer/ExplorerSelectorsTests.ts b/browser/test/Services/Explorer/ExplorerSelectorsTests.ts index 333c68c4b2..d2bfdc8a90 100644 --- a/browser/test/Services/Explorer/ExplorerSelectorsTests.ts +++ b/browser/test/Services/Explorer/ExplorerSelectorsTests.ts @@ -88,4 +88,41 @@ describe("ExplorerSelectors", () => { assert.deepEqual(result, expectedResult) }) }) + + describe("mapStateToNodeList", () => { + it("expands the root container", () => { + const state: ExplorerState.IExplorerState = { + ...ExplorerState.DefaultExplorerState, + rootFolder: { + type: "folder", + fullPath: "rootPath", + }, + expandedFolders: { + rootPath: [], + }, + } + + const result = ExplorerSelectors.mapStateToNodeList(state) + + const container = result[0] as ExplorerSelectors.IContainerNode + assert.strictEqual(container.type, "container") + assert.strictEqual(container.expanded, true) + }) + + it("collapses the root container", () => { + const state: ExplorerState.IExplorerState = { + ...ExplorerState.DefaultExplorerState, + rootFolder: { + type: "folder", + fullPath: "rootPath", + }, + expandedFolders: {}, + } + const result = ExplorerSelectors.mapStateToNodeList(state) + + const container = result[0] as ExplorerSelectors.IContainerNode + assert.strictEqual(container.type, "container") + assert.strictEqual(container.expanded, false) + }) + }) }) From 4faf405eee0231bb0b6922a8255e3502be398990 Mon Sep 17 00:00:00 2001 From: Kazuyuki Kamata Date: Sat, 9 Jun 2018 09:41:02 +0900 Subject: [PATCH 082/102] Fix less-than sign problem in IME (#2296) --- browser/src/Input/KeyboardInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/src/Input/KeyboardInput.tsx b/browser/src/Input/KeyboardInput.tsx index 08a9e4288d..343df35ad8 100644 --- a/browser/src/Input/KeyboardInput.tsx +++ b/browser/src/Input/KeyboardInput.tsx @@ -185,7 +185,7 @@ export class KeyboardInputView extends React.PureComponent< return } - const isMetaCommand = key.length > 1 + const isMetaCommand = key.length > 1 && key !== "" // We'll let the `input` handler take care of it, // unless it is a keystroke containing meta characters @@ -242,7 +242,7 @@ export class KeyboardInputView extends React.PureComponent< const valueLength = this._keyboardElement.value.length if (!this.state.isComposing && valueLength > 0) { - this._commit(this._keyboardElement.value) + this._commit(this._keyboardElement.value.replace("<", "")) } } From c0edfed3f5f51f24fad8aea6629850a039cdf09c Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Fri, 8 Jun 2018 18:26:32 -0700 Subject: [PATCH 083/102] Add Phil as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 5755f6502c..abb516cfb8 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -128,6 +128,7 @@ Thanks you to all our backers for making Oni possible! * James Herdman * Wayan Jimmy * Alex +* Phil Plückthun From 0891d22ab8452ea4f3652e821dcccc501fdf728a Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sat, 9 Jun 2018 03:54:46 +0000 Subject: [PATCH 084/102] Make FileSystem.readdir more robust (#2301) This specific change is motivated by a broken symlink on Linux, which will show up in an fs.readdir but will cause an error with fs.stat. If there are other situations where fs.stat will produce an error on a result returned from fs.readdir, this is probably the right thing to do in those as well. --- .../Services/Explorer/ExplorerFileSystem.ts | 7 ++- .../Explorer/ExplorerFileSystemTests.ts | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/browser/src/Services/Explorer/ExplorerFileSystem.ts b/browser/src/Services/Explorer/ExplorerFileSystem.ts index 9f716fa0fe..d651e6f6a5 100644 --- a/browser/src/Services/Explorer/ExplorerFileSystem.ts +++ b/browser/src/Services/Explorer/ExplorerFileSystem.ts @@ -60,8 +60,11 @@ export class FileSystem implements IFileSystem { const filesAndFolders = files.map(async f => { const fullPath = path.join(directoryPath, f) - const stat = await this._fs.stat(fullPath) - if (stat.isDirectory()) { + const isDirectory = await this._fs + .stat(fullPath) + .then(stat => stat.isDirectory()) + .catch(() => false) + if (isDirectory) { return { type: "folder", fullPath, diff --git a/browser/test/Services/Explorer/ExplorerFileSystemTests.ts b/browser/test/Services/Explorer/ExplorerFileSystemTests.ts index 15daaa1cd7..a9875d01e6 100644 --- a/browser/test/Services/Explorer/ExplorerFileSystemTests.ts +++ b/browser/test/Services/Explorer/ExplorerFileSystemTests.ts @@ -100,3 +100,59 @@ describe("File System tests", async () => { } }) }) + +describe("readdir", () => { + it("should not error if directory contains a broken symlink", async () => { + const goodDir = path.join("fake_dir", "good_dir") + const goodFile = path.join("fake_dir", "good_file") + const badFile = path.join("fake_dir", "bad_file") + + const fileSystem = new FileSystem({ + readdir(dirPath: string, callback: (err?: Error, result?: string[]) => void) { + assert.strictEqual(dirPath, "fake_dir") + callback(null, ["good_dir", "good_file", "bad_file"]) + }, + stat(targetPath: string, callback: (err?: Error, result?: any) => void) { + switch (targetPath) { + case goodDir: + return callback(null, { + isDirectory() { + return true + }, + }) + case goodFile: + return callback(null, { + isDirectory() { + return false + }, + }) + default: + // On Linux at least, an fs.stat call to a missing file and a broken symlink both result in this error: + return callback( + new Error(`ENOENT: no such file or directory, stat ${targetPath}`), + ) + } + }, + exists(targetPath: string, callback: any) { + assert.fail("Should not be used") + }, + } as any) + + const expected = [ + { + type: "folder", + fullPath: goodDir, + }, + { + type: "file", + fullPath: goodFile, + }, + { + type: "file", + fullPath: badFile, + }, + ] + const result = await fileSystem.readdir("fake_dir") + assert.deepEqual(result, expected) + }) +}) From fc7ea417962f634a37f0887f9bfd5c1743d532ae Mon Sep 17 00:00:00 2001 From: Ryan C Date: Sat, 9 Jun 2018 09:46:07 +0100 Subject: [PATCH 085/102] Toggle sidebar item on click. (#2297) --- browser/src/Services/Sidebar/SidebarStore.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/src/Services/Sidebar/SidebarStore.ts b/browser/src/Services/Sidebar/SidebarStore.ts index 43931a8e95..8a0757a1fc 100644 --- a/browser/src/Services/Sidebar/SidebarStore.ts +++ b/browser/src/Services/Sidebar/SidebarStore.ts @@ -104,13 +104,19 @@ export class SidebarManager { public setActiveEntry(id: string): void { if (id) { + const oldId = this._store.getState().activeEntryId + this._store.dispatch({ type: "SET_ACTIVE_ID", activeEntryId: id, }) - if (!this._contentSplit.isVisible) { + if (oldId !== id) { + this._contentSplit.show() + } else if (!this._contentSplit.isVisible) { this._contentSplit.show() + } else if (this._contentSplit.isVisible) { + this._contentSplit.hide() } } } From a02709c73fc664310169d6793206b033955aacf4 Mon Sep 17 00:00:00 2001 From: Tal Amuyal Date: Sat, 9 Jun 2018 13:42:39 +0300 Subject: [PATCH 086/102] Fix clipboard content trimming (#2298) --- browser/src/Editor/NeovimEditor/NeovimEditor.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index 52416f8c49..a31f9a6f26 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -4,6 +4,7 @@ * IEditor implementation for Neovim */ +import * as os from "os" import * as React from "react" import "rxjs/add/observable/defer" @@ -414,7 +415,9 @@ export class NeovimEditor extends Editor implements IEditor { const isAllowed = isYankAndAllowed || isDeleteAndAllowed if (isAllowed) { - clipboard.writeText(yankInfo.regcontents.join(require("os").EOL)) + const content = yankInfo.regcontents.join(os.EOL) + const postfix = yankInfo.regtype === "V" ? os.EOL : "" + clipboard.writeText(content + postfix) } } }), From b066c6be9ffc48d3937287e3db4cd5e0de37abb6 Mon Sep 17 00:00:00 2001 From: Brian Yu Date: Sat, 9 Jun 2018 11:42:59 -0400 Subject: [PATCH 087/102] #1978 Tutorial: f, F, t, T, ;, ',' (#2294) * Create tutorial for f, F, t, T, ;, ',' (#1978) * #1978 Remove references to [count], improve wording, and fix awkward examples --- .../src/Services/Learning/Tutorial/Notes.tsx | 70 +++++++++++++++++ .../Tutorials/InlineFindingTutorial.tsx | 75 +++++++++++++++++++ .../Learning/Tutorial/Tutorials/index.tsx | 2 + 3 files changed, 147 insertions(+) create mode 100644 browser/src/Services/Learning/Tutorial/Tutorials/InlineFindingTutorial.tsx diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index 9f4ce5c042..f32d0fbca2 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -438,3 +438,73 @@ export const Targetlkey = (): JSX.Element => { /> ) } + +export const fKey = (): JSX.Element => { + return ( + + + char: Moves cursor to next occurence of [char]. + + } + /> + ) +} + +export const FKey = (): JSX.Element => { + return ( + + + char: Moves cursor to previous occurence of [char]. + + } + /> + ) +} + +export const tKey = (): JSX.Element => { + return ( + + + char: Moves cursor to before the next occurence of [char]. + + } + /> + ) +} + +export const TKey = (): JSX.Element => { + return ( + + + char: Moves cursor to after the previous occurence of [char]. + + } + /> + ) +} + +export const RepeatKey = (): JSX.Element => { + return ( + Repeats last f, t, F, or T.} + /> + ) +} + +export const RepeatOppositeKey = (): JSX.Element => { + return ( + Repeats last f, t, F, or T in the opposite direction.} + /> + ) +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/InlineFindingTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/InlineFindingTutorial.tsx new file mode 100644 index 0000000000..aa33c50e66 --- /dev/null +++ b/browser/src/Services/Learning/Tutorial/Tutorials/InlineFindingTutorial.tsx @@ -0,0 +1,75 @@ +/** + * TutorialManager + */ + +import * as React from "react" + +import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" +// import { InitializeBufferStage, MoveToGoalStage } from "./../Stages" + +import * as Notes from "./../Notes" +import * as Stages from "./../Stages" + +const Line1 = "Use 'f' to move to the next occurrence of a character within the same line." +const Line2 = "And use 'F' to move to the previous occurrence of a character." +const Line3 = "'t' is like 'f' except it moves to one spot before the character." +const Line4 = "And 'T' is like 'F' except it moves one spot after." +const Line5 = "Awesome! You can also use ';' to repeat the last f, t, F, or T." +const Line6 = "Now use ',' to repeat the last f, t, F, or T in the opposite direction." + +export class InlineFindingTutorial implements ITutorial { + private _stages: ITutorialStage[] + + constructor() { + this._stages = [ + new Stages.SetBufferStage([Line1]), + new Stages.MoveToGoalStage("Move to the 'n' in 'next' using 'fn'", 0, 23), + new Stages.SetBufferStage([Line1, Line2]), + new Stages.MoveToGoalStage("Use 'j' to move down a line", 1, 23), + new Stages.MoveToGoalStage("Use 'F' to move LEFT to the goal", 1, 15), + new Stages.SetBufferStage([Line1, Line2, Line3]), + new Stages.MoveToGoalStage("Use 'j' to move down a line", 2, 15), + new Stages.MoveToGoalStage("Move to the character before 'b' using 'tb'", 2, 43), + new Stages.SetBufferStage([Line1, Line2, Line3, Line4]), + new Stages.MoveToGoalStage("Use 'j' to move down a line", 3, 43), + new Stages.MoveToGoalStage("Use 'T' to move to the goal", 3, 12), + new Stages.SetBufferStage([Line1, Line2, Line3, Line4, Line5]), + new Stages.MoveToGoalStage("Use 'j' to move down a line", 4, 12), + new Stages.MoveToGoalStage("Use 'f' to move to the goal", 4, 24), + new Stages.MoveToGoalStage("Use ';' to move to the goal", 4, 34), + new Stages.MoveToGoalStage("Use ';' to move to the goal", 4, 36), + new Stages.MoveToGoalStage("Use ';' to move to the goal", 4, 42), + new Stages.SetBufferStage([Line1, Line2, Line3, Line4, Line5, Line6]), + new Stages.MoveToGoalStage("Use 'j' to move down a line", 5, 42), + new Stages.MoveToGoalStage("Use ',' to move to the goal", 5, 24), + new Stages.MoveToGoalStage("Use ',' to move to the goal", 5, 18), + new Stages.MoveToGoalStage("Use ',' to move to the goal", 5, 16), + new Stages.MoveToGoalStage("Use ',' to move to the goal", 5, 6), + ] + } + + public get metadata(): ITutorialMetadata { + return { + id: "oni.tutorials.inline_finding", + name: "Motion: f, F, t, T", + description: + "Sometimes you need to move faster than 'h' and 'l' allow you to but need more control than 'w', 'e', and 'b', especially when using different operators. 'f' moves to a specific character to the right of the cursor, 'F' moves to a specific character to the left, and ';' and ',' allow you to repeat these motions in different directions.", + level: 145, + } + } + + public get notes(): JSX.Element[] { + return [ + , + , + , + , + , + , + ] + } + + public get stages(): ITutorialStage[] { + return this._stages + } +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx index 15dabf3671..1763a478e9 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx @@ -10,6 +10,7 @@ import { ChangeOperatorTutorial } from "./ChangeOperatorTutorial" import { CopyPasteTutorial } from "./CopyPasteTutorial" import { DeleteCharacterTutorial } from "./DeleteCharacterTutorial" import { DeleteOperatorTutorial } from "./DeleteOperatorTutorial" +import { InlineFindingTutorial } from "./InlineFindingTutorial" import { InsertAndUndoTutorial } from "./InsertAndUndoTutorial" import { SearchInBufferTutorial } from "./SearchInBufferTutorial" import { SwitchModeTutorial } from "./SwitchModeTutorial" @@ -35,4 +36,5 @@ export const AllTutorials: ITutorial[] = [ new ChangeOperatorTutorial(), new VisualModeTutorial(), new TargetsVimPluginTutorial(), + new InlineFindingTutorial(), ] From 070a3b9878cf9a6edfcbcf4c66b68e13e264ecd9 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Sat, 9 Jun 2018 09:19:36 -0700 Subject: [PATCH 088/102] Add Norikazu Hayashi as a backer - thank you! :) --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index abb516cfb8..846712cef8 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -129,6 +129,7 @@ Thanks you to all our backers for making Oni possible! * Wayan Jimmy * Alex * Phil Plückthun +* Norikazu Hayashi From 99d0247c5291c8dab384dae4554b52ecc091c0e9 Mon Sep 17 00:00:00 2001 From: Akin Date: Mon, 11 Jun 2018 13:04:24 +0100 Subject: [PATCH 089/102] Feature/add bindings to change sidebar width (#2236) * add commands to resize sidebar * remove console.logs * add tests for change size function * pass null as default and add test to handle incorrect input * add more test cases and default unit value * add extra test to ensure a unit is inserted * add max limits if sidebar is too big or small * use ternary to return limit * add tests to ensure that if at the bottom or top limit can still increase or decrease depending on if top limit or bottom * merge upstream fix conflicts * switch default keybindigs for linux/windows --- README.md | 106 +++++++++---------- browser/src/Input/KeyBindings.ts | 4 + browser/src/Services/Sidebar/SidebarStore.ts | 70 +++++++++++- browser/src/Services/Sidebar/index.ts | 18 +++- ui-tests/SidebarStore.test.ts | 50 +++++++++ ui-tests/mocks/Configuration.ts | 2 +- vim/core/typescript-vim | 2 +- vim/default/bundle/targets.vim | 2 +- vim/default/bundle/vim-commentary | 2 +- vim/default/bundle/vim-surround | 2 +- vim/default/bundle/vim-unimpaired | 2 +- 11 files changed, 195 insertions(+), 65 deletions(-) create mode 100644 ui-tests/SidebarStore.test.ts diff --git a/README.md b/README.md index a80e794aae..1f61912bc9 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Oni is an independent, MIT-licensed open source project. Please consider supporting Oni by: -* [Become a backer or sponsor on Patreon](https://www.patreon.com/onivim) -* [Become a backer or sponsor on Open Collective](https://opencollective.com/oni) -* [Become a backer on BountySource](https://www.bountysource.com/teams/oni) +* [Become a backer or sponsor on Patreon](https://www.patreon.com/onivim) +* [Become a backer or sponsor on Open Collective](https://opencollective.com/oni) +* [Become a backer on BountySource](https://www.bountysource.com/teams/oni)

    Sponsors via OpenCollective

    @@ -65,13 +65,13 @@ Check out [Releases](https://github.com/onivim/oni/releases) for the latest bina Oni brings several IDE-like integrations to neovim: -* [Embedded Browser](https://github.com/onivim/oni/wiki/Features#embedded-browser) -* [Quick Info](https://github.com/onivim/oni/wiki/Features#quick-info) -* [Code Completion](https://github.com/onivim/oni/wiki/Features#code-completion) -* [Syntax / Compilation Errors](https://github.com/onivim/oni/wiki/Features#syntax--compilation-errors) -* [Fuzzy Finding](https://github.com/onivim/oni/wiki/Features#fuzzy-finder) -* [Status Bar](https://github.com/onivim/oni/wiki/Features#status-bar) -* [Interactive Tutorial](https://github.com/onivim/oni/wiki/Features#interactive-tutorial) +* [Embedded Browser](https://github.com/onivim/oni/wiki/Features#embedded-browser) +* [Quick Info](https://github.com/onivim/oni/wiki/Features#quick-info) +* [Code Completion](https://github.com/onivim/oni/wiki/Features#code-completion) +* [Syntax / Compilation Errors](https://github.com/onivim/oni/wiki/Features#syntax--compilation-errors) +* [Fuzzy Finding](https://github.com/onivim/oni/wiki/Features#fuzzy-finder) +* [Status Bar](https://github.com/onivim/oni/wiki/Features#status-bar) +* [Interactive Tutorial](https://github.com/onivim/oni/wiki/Features#interactive-tutorial) And more coming - check out our [Roadmap](https://github.com/onivim/oni/wiki/Roadmap) @@ -83,9 +83,9 @@ Oni is cross-platform and supports Windows, Mac, and Linux. We have installation guides for each platform: -* [Windows](https://github.com/onivim/oni/wiki/Installation-Guide#windows) -* [Mac](https://github.com/onivim/oni/wiki/Installation-Guide#mac) -* [Linux](https://github.com/onivim/oni/wiki/Installation-Guide#linux) +* [Windows](https://github.com/onivim/oni/wiki/Installation-Guide#windows) +* [Mac](https://github.com/onivim/oni/wiki/Installation-Guide#mac) +* [Linux](https://github.com/onivim/oni/wiki/Installation-Guide#linux) The latest binaries are available on our [Releases](https://github.com/onivim/oni/releases) page, and if you'd prefer to build from source, check out our [Development](https://github.com/onivim/oni/wiki/Development) guide. @@ -93,12 +93,12 @@ The latest binaries are available on our [Releases](https://github.com/onivim/on The goal of this project is to provide both the full-fledged Vim experience, with no compromises, while pushing forward to enable new productivity scenarios. -* **Modern UX** - The Vim experience should not be compromised by terminal limitations. -* **Rich plugin development** - using JavaScript, instead of VimL. -* **Cross-platform support** - across Windows, OS X, and Linux. -* **Batteries included** - rich features are available out of the box - minimal setup needed to be productive. -* **Performance** - no compromises, Vim is fast, and Oni should be fast too. -* **Ease Learning Curve** - without sacrificing the Vim experience. +* **Modern UX** - The Vim experience should not be compromised by terminal limitations. +* **Rich plugin development** - using JavaScript, instead of VimL. +* **Cross-platform support** - across Windows, OS X, and Linux. +* **Batteries included** - rich features are available out of the box - minimal setup needed to be productive. +* **Performance** - no compromises, Vim is fast, and Oni should be fast too. +* **Ease Learning Curve** - without sacrificing the Vim experience. Vim is an incredible tool for manipulating _text_ at the speed of thought. With a composable, modal command language, it is no wonder that Vim usage is still prevalent today. @@ -108,44 +108,44 @@ The goal of this project is to give an editor that gives the best of both worlds ## Documentation -* Check out the [Wiki](https://github.com/onivim/oni/wiki) for documentation on how to use and modify Oni. -* [FAQ](https://github.com/onivim/oni/wiki/FAQ) -* [Roadmap](https://github.com/onivim/oni/wiki/Roadmap) +* Check out the [Wiki](https://github.com/onivim/oni/wiki) for documentation on how to use and modify Oni. +* [FAQ](https://github.com/onivim/oni/wiki/FAQ) +* [Roadmap](https://github.com/onivim/oni/wiki/Roadmap) ## Contributing There many ways to get involved & contribute to Oni: -* Support Oni financially by making a donation via: - * [Patreon](https://patreon.com/onivim) - * [OpenCollective](https://opencollective.com/oni) - * [Bountysource](https://salt.bountysource.com/teams/oni) -* Thumbs up existing [issues](https://github.com/onivim/oni/issues) if they impact you. -* [Create an issue](https://github.com/onivim/oni/issues) for bugs or new features. -* Review and update our [documentation](https://github.com/onivim/oni/wiki). -* Try out the latest [released build](https://github.com/onivim/oni/releases). -* Help us [develop](https://github.com/onivim/oni/wiki/Development): - * Review [PRs](https://github.com/onivim/oni/pulls) - * Submit a bug fix or feature - * Add test cases -* Create a blog post or YouTube video -* Follow us on [Twitter](https://twitter.com/oni_vim) +* Support Oni financially by making a donation via: + * [Patreon](https://patreon.com/onivim) + * [OpenCollective](https://opencollective.com/oni) + * [Bountysource](https://salt.bountysource.com/teams/oni) +* Thumbs up existing [issues](https://github.com/onivim/oni/issues) if they impact you. +* [Create an issue](https://github.com/onivim/oni/issues) for bugs or new features. +* Review and update our [documentation](https://github.com/onivim/oni/wiki). +* Try out the latest [released build](https://github.com/onivim/oni/releases). +* Help us [develop](https://github.com/onivim/oni/wiki/Development): + * Review [PRs](https://github.com/onivim/oni/pulls) + * Submit a bug fix or feature + * Add test cases +* Create a blog post or YouTube video +* Follow us on [Twitter](https://twitter.com/oni_vim) ## Acknowledgements Oni is an independent project and is made possible by the support of some exceptional people. Big thanks to the following people for helping to realize this project: -* the [neovim team](https://neovim.io/), especially [justinmk](https://github.com/justinmk) and [tarruda](https://github.com/tarruda) - Oni would not be possible without their vision -* [jordwalke](https://github.com/jordwalke) for his generous support, inspiration, and ideas. And React ;) -* [keforbes](https://github.com/keforbes) for helping to get this project off the ground -* [Akin909](https://github.com/Akin909) for his extensive contributions -* [CrossR](https://github.com/CrossR) for polishing features and configurations -* [Cryza](https://github.com/Cryza) for the webgl renderer -* [tillarnold](https://github.com/tillarnold) for giving us the `oni` npm package name -* [mhartington](https://github.com/mhartington) for his generous support -* [badosu](https://github.com/badosu) for his support, contributions, and managing the AUR releases -* All our current monthly [sponsors](https://salt.bountysource.com/teams/oni/supporters) and [backers](BACKERS.md) -* All of our [contributors](https://github.com/onivim/oni/graphs/contributors) - thanks for helping to improve this project! +* the [neovim team](https://neovim.io/), especially [justinmk](https://github.com/justinmk) and [tarruda](https://github.com/tarruda) - Oni would not be possible without their vision +* [jordwalke](https://github.com/jordwalke) for his generous support, inspiration, and ideas. And React ;) +* [keforbes](https://github.com/keforbes) for helping to get this project off the ground +* [Akin909](https://github.com/Akin909) for his extensive contributions +* [CrossR](https://github.com/CrossR) for polishing features and configurations +* [Cryza](https://github.com/Cryza) for the webgl renderer +* [tillarnold](https://github.com/tillarnold) for giving us the `oni` npm package name +* [mhartington](https://github.com/mhartington) for his generous support +* [badosu](https://github.com/badosu) for his support, contributions, and managing the AUR releases +* All our current monthly [sponsors](https://salt.bountysource.com/teams/oni/supporters) and [backers](BACKERS.md) +* All of our [contributors](https://github.com/onivim/oni/graphs/contributors) - thanks for helping to improve this project! Several other great neovim front-end UIs [here](https://github.com/neovim/neovim/wiki/Related-projects) served as a reference, especially [NyaoVim](https://github.com/rhysd/NyaoVim) and [VimR](https://github.com/qvacua/vimr). I encourage you to check those out! @@ -166,9 +166,9 @@ Windows and OSX have a bundled version of Neovim, which is covered under [Neovim Bundled plugins have their own license terms. These include: -* [typescript-vim](https://github.com/leafgarland/typescript-vim) (`oni/vim/core/typescript.vim`) -* [targets.vim](https://github.com/wellle/targets.vim) (`oni/vim/default/bundle/targets.vim`) -* [vim-commentary](https://github.com/tpope/vim-commentary) (`oni/vim/default/bundle/vim-commentary`) -* [vim-unimpaired](https://github.com/tpope/vim-unimpaired) (`oni/vim/default/bundle/vim-unimpaired`) -* [vim-surround](https://github.com/tpope/vim-surround) (`oni/vim/default/bundle/vim-surround`) -* [vim-reasonml](https://github.com/reasonml-editor/vim-reason) (`.vim` files in `oni/vim/core/oni-plugin-reasonml`) +* [typescript-vim](https://github.com/leafgarland/typescript-vim) (`oni/vim/core/typescript.vim`) +* [targets.vim](https://github.com/wellle/targets.vim) (`oni/vim/default/bundle/targets.vim`) +* [vim-commentary](https://github.com/tpope/vim-commentary) (`oni/vim/default/bundle/vim-commentary`) +* [vim-unimpaired](https://github.com/tpope/vim-unimpaired) (`oni/vim/default/bundle/vim-unimpaired`) +* [vim-surround](https://github.com/tpope/vim-surround) (`oni/vim/default/bundle/vim-surround`) +* [vim-reasonml](https://github.com/reasonml-editor/vim-reason) (`.vim` files in `oni/vim/core/oni-plugin-reasonml`) diff --git a/browser/src/Input/KeyBindings.ts b/browser/src/Input/KeyBindings.ts index 3f5a26d1b2..f0ed4d9636 100644 --- a/browser/src/Input/KeyBindings.ts +++ b/browser/src/Input/KeyBindings.ts @@ -47,6 +47,8 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati input.bind("", "oni.editor.hide") input.bind("", "buffer.toggle") input.bind("", "search.searchAllFiles") + input.bind("", "sidebar.decreaseWidth") + input.bind("", "sidebar.increaseWidth") input.bind("", "oni.config.openConfigJs") if (config.getValue("editor.clipboard.enabled")) { @@ -60,6 +62,8 @@ export const applyDefaultKeyBindings = (oni: Oni.Plugin.Api, config: Configurati input.bind("", "browser.reload") } else { input.bind("", "oni.quit") + input.bind("", "sidebar.decreaseWidth") + input.bind("", "sidebar.increaseWidth") input.bind("", "quickOpen.show", () => isNormalMode() && !isMenuOpen()) input.bind("", "commands.show", isNormalMode) input.bind("", "language.codeAction.expand") diff --git a/browser/src/Services/Sidebar/SidebarStore.ts b/browser/src/Services/Sidebar/SidebarStore.ts index 8a0757a1fc..0b61719a9b 100644 --- a/browser/src/Services/Sidebar/SidebarStore.ts +++ b/browser/src/Services/Sidebar/SidebarStore.ts @@ -7,7 +7,8 @@ import { Reducer, Store } from "redux" import { createStore as createReduxStore } from "./../../Redux" -import { configuration } from "../Configuration" +import { Configuration } from "../Configuration" +import { DefaultConfiguration } from "../Configuration/DefaultConfiguration" import { WindowManager, WindowSplitHandle } from "./../WindowManager" import { SidebarContentSplit } from "./SidebarContentSplit" import { SidebarSplit } from "./SidebarSplit" @@ -65,15 +66,19 @@ export class SidebarManager { return this._store } - constructor(private _windowManager: WindowManager = null) { + constructor( + private _windowManager: WindowManager = null, + private _configuration: Configuration, + ) { this._store = createStore() - configuration.onConfigurationChanged.subscribe(val => { + this._configuration.onConfigurationChanged.subscribe(val => { if (typeof val["sidebar.width"] === "string") { this.setWidth(val["sidebar.width"]) } }) - this.setWidth(configuration.getValue("sidebar.width")) + + this.setWidth(this._configuration.getValue("sidebar.width")) if (_windowManager) { this._iconSplit = this._windowManager.createSplit("left", new SidebarSplit(this)) @@ -84,6 +89,18 @@ export class SidebarManager { } } + public increaseWidth(): void { + if (this._contentSplit.isVisible) { + this.store.dispatch({ type: "INCREASE_WIDTH" }) + } + } + + public decreaseWidth(): void { + if (this._contentSplit.isVisible) { + this.store.dispatch({ type: "DECREASE_WIDTH" }) + } + } + public setWidth(width: string): void { if (width) { this._store.dispatch({ @@ -190,6 +207,41 @@ export type SidebarActions = | { type: "LEAVE" } + | { + type: "INCREASE_WIDTH" + } + | { + type: "DECREASE_WIDTH" + } + +export const changeSize = (change: "increase" | "decrease") => ( + size: string, + defaultValue = DefaultConfiguration["sidebar.width"], +): string => { + const [numberString, letters = "em"] = size.match(/[a-zA-Z]+|[0-9]+/g) + const isAllowedUnit = ["em", "px", "vw"].includes(letters) + const unitsToUse = isAllowedUnit ? letters : "em" + const convertedNumber = Number(numberString) + if (isNaN(convertedNumber)) { + return defaultValue + } + + // If too small don't allow a decrease and vice versa + const tooSmall = convertedNumber - 1 < 1 && change === "decrease" + const tooBig = convertedNumber + 1 > 50 && change === "increase" + + const changed = + tooBig || tooSmall + ? convertedNumber + : change === "increase" + ? convertedNumber + 1 + : convertedNumber - 1 + + return `${changed}${unitsToUse}` +} + +export const increaseWidth = changeSize("increase") +export const decreaseWidth = changeSize("decrease") export const sidebarReducer: Reducer = ( state: ISidebarState = DefaultSidebarState, @@ -230,6 +282,16 @@ export const sidebarReducer: Reducer = ( } else { return newState } + case "DECREASE_WIDTH": + return { + ...newState, + width: decreaseWidth(newState.width), + } + case "INCREASE_WIDTH": + return { + ...newState, + width: increaseWidth(newState.width), + } default: return newState } diff --git a/browser/src/Services/Sidebar/index.ts b/browser/src/Services/Sidebar/index.ts index cddc5d2f35..d8be7f6e1d 100644 --- a/browser/src/Services/Sidebar/index.ts +++ b/browser/src/Services/Sidebar/index.ts @@ -11,11 +11,25 @@ export * from "./SidebarStore" export const activate = (configuration: Configuration, workspace: Workspace) => { if (configuration.getValue("sidebar.enabled")) { - _sidebarManager = new SidebarManager(windowManager) + _sidebarManager = new SidebarManager(windowManager, configuration) if (!configuration.getValue("sidebar.default.open")) { _sidebarManager.toggleSidebarVisibility() } + commandManager.registerCommand({ + command: "sidebar.increaseWidth", + name: "Sidebar: Increase Width", + detail: "Increase the width of the sidebar pane", + execute: () => _sidebarManager.increaseWidth(), + }) + + commandManager.registerCommand({ + command: "sidebar.decreaseWidth", + name: "Sidebar: Decrease Width", + detail: "Decrease the width of the sidebar pane", + execute: () => _sidebarManager.decreaseWidth(), + }) + commandManager.registerCommand({ command: "sidebar.toggle", name: "Sidebar: Toggle", @@ -23,7 +37,7 @@ export const activate = (configuration: Configuration, workspace: Workspace) => execute: () => _sidebarManager.toggleSidebarVisibility(), }) } else { - _sidebarManager = new SidebarManager() + _sidebarManager = new SidebarManager(null, configuration) } } diff --git a/ui-tests/SidebarStore.test.ts b/ui-tests/SidebarStore.test.ts new file mode 100644 index 0000000000..989e41f0cc --- /dev/null +++ b/ui-tests/SidebarStore.test.ts @@ -0,0 +1,50 @@ +import { + decreaseWidth, + increaseWidth, + sidebarReducer, +} from "./../browser/src/Services/Sidebar/SidebarStore" + +describe("Change size function", () => { + it("Should correctly return an increased size", () => { + const newSize = increaseWidth("12em", null) + expect(newSize).toBe("13em") + }) + + it("Should correctly return an decreased size", () => { + const newSize = decreaseWidth("12em", null) + expect(newSize).toBe("11em") + }) + + it("Should return the original size if passed an invalid value", () => { + const newSize = increaseWidth("appleem", "15em") + expect(newSize).toBe("15em") + }) + it("Should return the last value if the user input is too big", () => { + const newSize = increaseWidth("50em", "15em") + expect(newSize).toBe("50em") + }) + + it("Should return the last value if the user input is too small", () => { + const newSize = decreaseWidth("1em", "15em") + expect(newSize).toBe("1em") + }) + + it("Should decrease a value if at top limit", () => { + const newSize = decreaseWidth("50em", null) + expect(newSize).toBe("49em") + }) + + it("Should increase a value if at bottom limit", () => { + const newSize = increaseWidth("1em", null) + expect(newSize).toBe("2em") + }) + + it("Should use a default unit if the user passes an invalid unit value", () => { + const newSize = decreaseWidth("15apples", "12em") + expect(newSize).toBe("14em") + }) + it("Should use default unit if the value passed in does not have any units", () => { + const newSize = increaseWidth("15", "11em") + expect(newSize).toBe("16em") + }) +}) diff --git a/ui-tests/mocks/Configuration.ts b/ui-tests/mocks/Configuration.ts index d8120d594a..765d2a5603 100644 --- a/ui-tests/mocks/Configuration.ts +++ b/ui-tests/mocks/Configuration.ts @@ -5,5 +5,5 @@ const Configuration = jest.fn().mockImplementation(() => { } }) -export const configuration = Configuration +export const configuration = new Configuration() export default Configuration diff --git a/vim/core/typescript-vim b/vim/core/typescript-vim index e25636b442..fbd0e9e508 160000 --- a/vim/core/typescript-vim +++ b/vim/core/typescript-vim @@ -1 +1 @@ -Subproject commit e25636b44211a4be7b089bfed7cf09aa7dd086f5 +Subproject commit fbd0e9e508535f7d89778f7280dc76505348dcc6 diff --git a/vim/default/bundle/targets.vim b/vim/default/bundle/targets.vim index c3042dc18a..dec6409fb8 160000 --- a/vim/default/bundle/targets.vim +++ b/vim/default/bundle/targets.vim @@ -1 +1 @@ -Subproject commit c3042dc18acc0dfcee479310d3efc6aefe92db75 +Subproject commit dec6409fb80ec1554bad582652f7511aa28c4ed2 diff --git a/vim/default/bundle/vim-commentary b/vim/default/bundle/vim-commentary index 7f2127b1df..89f43af186 160000 --- a/vim/default/bundle/vim-commentary +++ b/vim/default/bundle/vim-commentary @@ -1 +1 @@ -Subproject commit 7f2127b1dfc57811112785985b46ff2289d72334 +Subproject commit 89f43af18692d22ed999c3097e449f12fdd8b299 diff --git a/vim/default/bundle/vim-surround b/vim/default/bundle/vim-surround index 643a42454b..e49d6c2459 160000 --- a/vim/default/bundle/vim-surround +++ b/vim/default/bundle/vim-surround @@ -1 +1 @@ -Subproject commit 643a42454bc8c2b2735de14f309523ce733a5358 +Subproject commit e49d6c2459e0f5569ff2d533b4df995dd7f98313 diff --git a/vim/default/bundle/vim-unimpaired b/vim/default/bundle/vim-unimpaired index 4afeced83c..3a7759075c 160000 --- a/vim/default/bundle/vim-unimpaired +++ b/vim/default/bundle/vim-unimpaired @@ -1 +1 @@ -Subproject commit 4afeced83cb6bf32ba508fd57ad7dc45c96478f3 +Subproject commit 3a7759075cca5b0dc29ce81f2747489b6c8e36a7 From 71a79ea80ffc095539a03718baa7bb92105bba4e Mon Sep 17 00:00:00 2001 From: Tal Amuyal Date: Mon, 11 Jun 2018 22:01:15 +0300 Subject: [PATCH 090/102] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 64977ba765..78eb6f5b72 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,14 +1,14 @@ -Oni Version: -Neovim Version (Linux only): -Operating System: +**Oni Version:** +**Neovim Version (Linux only):** +**Operating System:** -#### Describe your issue +**Issue:** -#### Expected behaviour +**Expected behavior:** -#### Actual behaviour +**Actual behavior:** -#### Steps to reproduce +**Steps to reproduce:** From f5753e44daa90272d82951bf4655945b53e2083e Mon Sep 17 00:00:00 2001 From: "George T. Gougoudis" Date: Mon, 18 Jun 2018 00:14:44 +0300 Subject: [PATCH 091/102] [GrammarLoader] Properly check if key is in cache (#2321) --- browser/src/Services/SyntaxHighlighting/GrammarLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts b/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts index 0e90a623e4..528917aaf7 100644 --- a/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts +++ b/browser/src/Services/SyntaxHighlighting/GrammarLoader.ts @@ -38,7 +38,7 @@ export class GrammarLoader implements IGrammarLoader { return null } - if (this._grammarCache[language]) { + if (language in this._grammarCache) { return this._grammarCache[language] } From af331cdb153eab55106f79cb52e0c5d219806219 Mon Sep 17 00:00:00 2001 From: Ryan C Date: Mon, 18 Jun 2018 17:41:01 +0100 Subject: [PATCH 092/102] Bump the Neovim package version to get Nvim 0.3.0. (#2304) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e88f8aaa5c..6d57860b64 100644 --- a/package.json +++ b/package.json @@ -861,7 +861,7 @@ "msgpack-lite": "0.1.26", "ocaml-language-server": "^1.0.27", "oni-api": "^0.0.45", - "oni-neovim-binaries": "0.1.1", + "oni-neovim-binaries": "0.1.2", "oni-ripgrep": "0.0.4", "oni-types": "^0.0.8", "react": "^16.3.2", From 609ffb42540ee121800b43532d11f7a94c3706d9 Mon Sep 17 00:00:00 2001 From: Nick Neisen Date: Mon, 18 Jun 2018 13:18:51 -0600 Subject: [PATCH 093/102] #2238 - Change handling of --plugin-develop argument (#2266) * Change handling of --plugin-develop argument Add additional handling for the --plugin-develop argument so that no path and invalid path cases are handled. * Improve --plugin-develop error feedback on startup Change the developer logging to be Log.warn instead of Log.info. Add a popup to notify the developer of an error once Oni has launched. --- browser/src/App.ts | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/browser/src/App.ts b/browser/src/App.ts index d1573da854..70ac8807fd 100644 --- a/browser/src/App.ts +++ b/browser/src/App.ts @@ -152,10 +152,25 @@ export const start = async (args: string[]): Promise => { const pluginManager = PluginManager.getInstance() const developmentPlugin = parsedArgs["plugin-develop"] + let developmentPluginError: { title: string; errorText: string } - if (developmentPlugin) { + if (typeof developmentPlugin === "string") { Log.info("Registering development plugin: " + developmentPlugin) - pluginManager.addDevelopmentPlugin(developmentPlugin) + if (fs.existsSync(developmentPlugin)) { + pluginManager.addDevelopmentPlugin(developmentPlugin) + } else { + developmentPluginError = { + title: "Error parsing arguments", + errorText: "Could not find plugin: " + developmentPlugin, + } + Log.warn(developmentPluginError.errorText) + } + } else if (typeof developmentPlugin === "boolean") { + developmentPluginError = { + title: "Error parsing arguments", + errorText: "--plugin-develop must be followed by a plugin path", + } + Log.warn(developmentPluginError.errorText) } Performance.startMeasure("Oni.Start.Plugins.Discover") @@ -216,6 +231,17 @@ export const start = async (args: string[]): Promise => { const Notifications = await notificationsPromise Notifications.activate(configuration, overlayManager) + if (typeof developmentPluginError !== "undefined") { + const notifications = Notifications.getInstance() + const notification = notifications.createItem() + notification.setContents(developmentPluginError.title, developmentPluginError.errorText) + notification.setLevel("error") + notification.onClick.subscribe(() => + commandManager.executeCommand("oni.config.openConfigJs"), + ) + notification.show() + } + configuration.onConfigurationError.subscribe(err => { const notifications = Notifications.getInstance() const notification = notifications.createItem() From 16c2eb508a5c69e4cc1322bd6395a02065ca5912 Mon Sep 17 00:00:00 2001 From: Bryan Phelps Date: Mon, 18 Jun 2018 18:09:12 -0700 Subject: [PATCH 094/102] Browser: Fix scroll key binding conflict (#2239) * Check for webview focus before running command on webview * Fix browser path * Hook up active tag management for the browser layer * Fix lint issues --- browser/src/Editor/BufferManager.ts | 16 +++- browser/src/Services/Browser/BrowserView.tsx | 21 +++++ browser/src/Services/Browser/index.tsx | 85 ++++++++++++++++---- browser/src/Services/FocusManager.ts | 4 + test/CiTests.ts | 7 +- test/ci/Browser.LocationTest.ts | 64 +++++++++++++++ webview_preload/src/index.ts | 12 +++ 7 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 test/ci/Browser.LocationTest.ts diff --git a/browser/src/Editor/BufferManager.ts b/browser/src/Editor/BufferManager.ts index 411447671d..6ae98f1c47 100644 --- a/browser/src/Editor/BufferManager.ts +++ b/browser/src/Editor/BufferManager.ts @@ -43,8 +43,14 @@ import { TokenColor } from "./../Services/TokenColors" import { IBufferLayer } from "./NeovimEditor/BufferLayerManager" +/** + * Candidate API methods + */ export interface IBuffer extends Oni.Buffer { setLanguage(lang: string): Promise + + getLayerById(id: string): T + getCursorPosition(): Promise handleInput(key: string): boolean detectIndentation(): Promise @@ -143,10 +149,12 @@ export class Buffer implements IBuffer { this._actions.addBufferLayer(parseInt(this._id, 10), layer) } - public getLayerById(id: string): T { - return (this._store - .getState() - .layers[parseInt(this._id, 10)].find(layer => layer.id === id) as any) as T + public getLayerById(id: string): T | null { + return ( + ((this._store + .getState() + .layers[parseInt(this._id, 10)].find(layer => layer.id === id) as any) as T) || null + ) } public removeLayer(layer: IBufferLayer): void { diff --git a/browser/src/Services/Browser/BrowserView.tsx b/browser/src/Services/Browser/BrowserView.tsx index f830e0239f..58dfb96ae3 100644 --- a/browser/src/Services/Browser/BrowserView.tsx +++ b/browser/src/Services/Browser/BrowserView.tsx @@ -69,6 +69,9 @@ export interface IBrowserViewProps { scrollDown: IEvent scrollLeft: IEvent scrollRight: IEvent + + webviewRef?: (webviewTag: WebviewTag) => void + onFocusTag?: (tagName: string | null) => void } export interface IBrowserViewState { @@ -324,6 +327,24 @@ export class BrowserView extends React.PureComponent { focusManager.popFocus(this._webviewElement) }) + + this._webviewElement.addEventListener("ipc-message", event => { + switch (event.channel) { + case "focusin": + if (this.props.onFocusTag) { + this.props.onFocusTag(event.args[0]) + } + return + case "focusout": + if (this.props.onFocusTag) { + this.props.onFocusTag(null) + } + } + }) + + if (this.props.webviewRef) { + this.props.webviewRef(this._webviewElement) + } } } } diff --git a/browser/src/Services/Browser/index.tsx b/browser/src/Services/Browser/index.tsx index 02d7b35856..0cf849ea18 100644 --- a/browser/src/Services/Browser/index.tsx +++ b/browser/src/Services/Browser/index.tsx @@ -4,15 +4,18 @@ * Entry point for browser integration plugin */ -import { shell } from "electron" +import { shell, WebviewTag } from "electron" import * as React from "react" import * as Oni from "oni-api" import { Event } from "oni-types" +import { IBuffer } from "./../../Editor/BufferManager" + import { CommandManager } from "./../CommandManager" import { Configuration } from "./../Configuration" import { EditorManager } from "./../EditorManager" +import { focusManager } from "./../FocusManager" import { AchievementsManager, getInstance as getAchievementsInstance, @@ -30,12 +33,23 @@ export class BrowserLayer implements Oni.BufferLayer { private _scrollRightEvent = new Event() private _scrollLeftEvent = new Event() + private _webview: WebviewTag | null = null + private _activeTagName: string | null = null + constructor(private _url: string, private _configuration: Configuration) {} public get id(): string { return "oni.browser" } + public get webviewElement(): HTMLElement { + return this._webview + } + + public get activeTagName(): string { + return this._activeTagName + } + public render(): JSX.Element { return ( (this._webview = webview)} + onFocusTag={newTag => (this._activeTagName = newTag)} /> ) } @@ -92,8 +108,6 @@ export const activate = ( ) => { let count = 0 - const activeLayers: { [bufferId: string]: BrowserLayer } = {} - const browserEnabledSetting = configuration.registerSetting("browser.enabled", { requiresReload: false, description: @@ -128,7 +142,6 @@ export const activate = ( const layer = new BrowserLayer(url, configuration) buffer.addLayer(layer) - activeLayers[buffer.id] = layer const achievements = getAchievementsInstance() achievements.notifyGoal("oni.goal.openBrowser") @@ -160,18 +173,58 @@ export const activate = ( detail: null, }) + const getLayerForBuffer = (buffer: Oni.Buffer): BrowserLayer => { + return (buffer as IBuffer).getLayerById("oni.browser") + } + const executeCommandForLayer = (callback: (browserLayer: BrowserLayer) => void) => () => { const activeBuffer = editorManager.activeEditor.activeBuffer - const browserLayer = activeLayers[activeBuffer.id] + const browserLayer = getLayerForBuffer(activeBuffer) if (browserLayer) { callback(browserLayer) } } - const isBrowserLayerActive = () => - !!activeLayers[editorManager.activeEditor.activeBuffer.id] && - browserEnabledSetting.getValue() + const isBrowserCommandEnabled = (): boolean => { + if (!browserEnabledSetting.getValue()) { + return false + } + + const layer = getLayerForBuffer(editorManager.activeEditor.activeBuffer) + if (!layer) { + return false + } + + // If the layer is open, but not focused, we shouldn't execute commands. + // This could happen if there is a pop-up menu, or if we're working with some + // non-webview UI in the browser (like the address bar) + if (layer.webviewElement !== focusManager.focusedElement) { + return false + } + + return true + } + + const isInputTag = (tagName: string): boolean => { + return tagName === "INPUT" || tagName === "TEXTAREA" + } + + const isBrowserScrollCommandEnabled = (): boolean => { + if (!isBrowserCommandEnabled()) { + return false + } + + const layer = getLayerForBuffer(editorManager.activeEditor.activeBuffer) + + // Finally, if the webview _is_ focused, but something has focus, we'll + // skip our bindings and defer to the browser + if (isInputTag(layer.activeTagName)) { + return false + } + + return true + } // Per-layer commands commandManager.registerCommand({ @@ -179,7 +232,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.openDebugger()), name: "Browser: Open DevTools", detail: "Open the devtools pane for the current browser window.", - enabled: isBrowserLayerActive, + enabled: isBrowserCommandEnabled, }) commandManager.registerCommand({ @@ -187,7 +240,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.goBack()), name: "Browser: Go back", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserCommandEnabled, }) commandManager.registerCommand({ @@ -195,7 +248,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.goForward()), name: "Browser: Go forward", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserCommandEnabled, }) commandManager.registerCommand({ @@ -203,7 +256,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.reload()), name: "Browser: Reload", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserCommandEnabled, }) commandManager.registerCommand({ @@ -211,7 +264,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.scrollDown()), name: "Browser: Scroll Down", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserScrollCommandEnabled, }) commandManager.registerCommand({ @@ -219,7 +272,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.scrollUp()), name: "Browser: Scroll Up", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserScrollCommandEnabled, }) commandManager.registerCommand({ @@ -227,7 +280,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.scrollLeft()), name: "Browser: Scroll Left", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserScrollCommandEnabled, }) commandManager.registerCommand({ @@ -235,7 +288,7 @@ export const activate = ( execute: executeCommandForLayer(browser => browser.scrollRight()), name: "Browser: Scroll Right", detail: "", - enabled: isBrowserLayerActive, + enabled: isBrowserScrollCommandEnabled, }) } diff --git a/browser/src/Services/FocusManager.ts b/browser/src/Services/FocusManager.ts index 3a9a90eb83..6321624c37 100644 --- a/browser/src/Services/FocusManager.ts +++ b/browser/src/Services/FocusManager.ts @@ -7,6 +7,10 @@ import * as Log from "oni-core-logging" class FocusManager { private _focusElementStack: HTMLElement[] = [] + public get focusedElement(): HTMLElement | null { + return this._focusElementStack.length > 0 ? this._focusElementStack[0] : null + } + public pushFocus(element: HTMLElement) { this._focusElementStack = [element, ...this._focusElementStack] diff --git a/test/CiTests.ts b/test/CiTests.ts index 293604259a..e125f713bb 100644 --- a/test/CiTests.ts +++ b/test/CiTests.ts @@ -8,6 +8,7 @@ import * as mkdirp from "mkdirp" import { IFailedTest, Oni, runInProcTest } from "./common" const LongTimeout = 5000 + const CiTests = [ // Core functionality tests "Api.Buffer.AddLayer", @@ -17,6 +18,8 @@ const CiTests = [ "AutoCompletionTest-HTML", "AutoCompletionTest-TypeScript", + "Browser.LocationTest", + "Configuration.JavaScriptEditorTest", "Configuration.TypeScriptEditor.NewConfigurationTest", "Configuration.TypeScriptEditor.CompletionTest", @@ -87,7 +90,9 @@ const FGYELLOW = "\x1b[33m" describe("ci tests", function() { const tests = Platform.isWindows() ? [...CiTests, ...WindowsOnlyTests] - : Platform.isMac() ? [...CiTests, ...OSXOnlyTests] : CiTests + : Platform.isMac() + ? [...CiTests, ...OSXOnlyTests] + : CiTests const testFailures: IFailedTest[] = [] tests.forEach(test => { diff --git a/test/ci/Browser.LocationTest.ts b/test/ci/Browser.LocationTest.ts new file mode 100644 index 0000000000..410aa0bc3a --- /dev/null +++ b/test/ci/Browser.LocationTest.ts @@ -0,0 +1,64 @@ +/** + * Test scripts for Auto Complete for a Typescript file. + */ + +import * as assert from "assert" + +import * as Oni from "oni-api" + +import { WebviewTag } from "electron" + +import { getElementsBySelector } from "./Common" + +export const test = async (oni: Oni.Plugin.Api) => { + await oni.automation.waitForEditors() + + const getWebView = (): WebviewTag | null => { + const elems = getElementsBySelector("webview") + return elems.length > 0 ? elems[0] : null + } + + const waitForWebViewUrl = (urlPart: string): boolean => { + const webview = getWebView() + + if (!webview) { + return false + } + + const url = webview.getURL() + + return url.indexOf(urlPart) >= 0 + } + + oni.commands.executeCommand("browser.openUrl.verticalSplit", "https://github.com/onivim/oni") + + await oni.automation.waitFor(() => getWebView() !== null) + await oni.automation.waitFor(() => waitForWebViewUrl("github.com")) + + await oni.automation.sendKeys("") + await oni.automation.sleep(500) + + // We'll sneak to the browser address and load a new site + const anyOni = oni as any + const sneak = anyOni.sneak.getSneakMatchingTag("browser.address") + + const keys: string = sneak.triggerKeys.toLowerCase() + await anyOni.automation.sendKeysV2(keys) + + await oni.automation.sleep(500) + + await anyOni.automation.sendKeysV2("https://www.onivim.io") + + await oni.automation.sleep(500) + + await anyOni.automation.sendKeysV2("") + + await oni.automation.waitFor(() => waitForWebViewUrl("onivim.io")) + + assert.ok( + getWebView() + .getURL() + .indexOf("onivim.io") >= 0, + "Successfully navigated to onivim.io", + ) +} diff --git a/webview_preload/src/index.ts b/webview_preload/src/index.ts index 8285f29072..5c0ef78998 100644 --- a/webview_preload/src/index.ts +++ b/webview_preload/src/index.ts @@ -5,9 +5,21 @@ * https://electronjs.org/docs/api/webview-tag#preload */ +declare var require: any ;(() => { const __oni_win: any = window + const { ipcRenderer } = require("electron") + + window.document.addEventListener("focusin", evt => { + const target = evt.target as HTMLElement + ipcRenderer.sendToHost("focusin", target ? target.tagName : null) + }) + + window.document.addEventListener("focusout", evt => { + ipcRenderer.sendToHost("focusout") + }) + interface Rectangle { x: number y: number From 7c09bcf76cf3cf7fc61d39c99b550f711f9a3d04 Mon Sep 17 00:00:00 2001 From: Ryan C Date: Tue, 19 Jun 2018 17:15:10 +0100 Subject: [PATCH 095/102] Markdown changes (#2282) * Fix a few small Markdown preview issues. * Close markdown preview on exit. * Update marked version. * Add config option for auto scroll. * Keep focus on initial split. * Fix toggle function. Before the fix was only applied to close. * Hook up clicking a link to open the Oni browser if activated. Fixes #2021. * Fix lint error. * Update for changed API. * Update tests. * Bump package number. * Update lock file. * Syntax change. --- browser/src/Services/Browser/index.tsx | 6 +++- .../Configuration/DefaultConfiguration.ts | 7 ++++- .../Configuration/IConfigurationValues.ts | 4 +++ .../Services/WindowManager/WindowManager.ts | 6 +++- .../WindowManager/WindowManagerTests.ts | 8 ++--- .../oni-plugin-markdown-preview/package.json | 2 +- .../oni-plugin-markdown-preview/src/index.tsx | 29 +++++++++++++++---- main/src/main.ts | 5 ++++ package.json | 4 +-- yarn.lock | 18 ++++++------ 10 files changed, 65 insertions(+), 24 deletions(-) diff --git a/browser/src/Services/Browser/index.tsx b/browser/src/Services/Browser/index.tsx index 0cf849ea18..d164714dd8 100644 --- a/browser/src/Services/Browser/index.tsx +++ b/browser/src/Services/Browser/index.tsx @@ -4,7 +4,7 @@ * Entry point for browser integration plugin */ -import { shell, WebviewTag } from "electron" +import { ipcRenderer, shell, WebviewTag } from "electron" import * as React from "react" import * as Oni from "oni-api" @@ -290,6 +290,10 @@ export const activate = ( detail: "", enabled: isBrowserScrollCommandEnabled, }) + + ipcRenderer.on("open-oni-browser", (event: string, args: string) => { + openUrl(args) + }) } export const registerAchievements = (achievements: AchievementsManager) => { diff --git a/browser/src/Services/Configuration/DefaultConfiguration.ts b/browser/src/Services/Configuration/DefaultConfiguration.ts index 79bdae8a29..2b9dc36174 100644 --- a/browser/src/Services/Configuration/DefaultConfiguration.ts +++ b/browser/src/Services/Configuration/DefaultConfiguration.ts @@ -57,6 +57,9 @@ const BaseConfiguration: IConfigurationValues = { "experimental.preview.enabled": false, "experimental.welcome.enabled": false, + "experimental.markdownPreview.enabled": false, + "experimental.markdownPreview.autoScroll": true, + "experimental.neovim.transport": "stdio", // TODO: Enable pipe transport for Windows // "experimental.neovim.transport": Platform.isWindows() ? "pipe" : "stdio", @@ -459,7 +462,9 @@ const LinuxConfigOverrides: Partial = { const PlatformConfigOverride = Platform.isWindows() ? WindowsConfigOverrides - : Platform.isLinux() ? LinuxConfigOverrides : MacConfigOverrides + : Platform.isLinux() + ? LinuxConfigOverrides + : MacConfigOverrides export const DefaultConfiguration = { ...BaseConfiguration, diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index 60eb8ad501..9bc579aea1 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -49,6 +49,10 @@ export interface IConfigurationValues { // Whether or not the learning pane is available "experimental.particles.enabled": boolean + // Whether the markdown preview pane should be shown + "experimental.markdownPreview.enabled": boolean + "experimental.markdownPreview.autoScroll": boolean + // The transport to use for Neovim // Valid values are "stdio" and "pipe" "experimental.neovim.transport": string diff --git a/browser/src/Services/WindowManager/WindowManager.ts b/browser/src/Services/WindowManager/WindowManager.ts index 5793f30c11..886135c3da 100644 --- a/browser/src/Services/WindowManager/WindowManager.ts +++ b/browser/src/Services/WindowManager/WindowManager.ts @@ -137,7 +137,11 @@ export class WindowManager { return this._store } - public get activeSplit(): IAugmentedSplitInfo { + get activeSplitHandle(): WindowSplitHandle { + return new WindowSplitHandle(this._store, this, this.activeSplit.id) + } + + private get activeSplit(): IAugmentedSplitInfo { const focusedSplit = this._store.getState().focusedSplitId if (!focusedSplit) { diff --git a/browser/test/Services/WindowManager/WindowManagerTests.ts b/browser/test/Services/WindowManager/WindowManagerTests.ts index 8e836d6c90..589d658892 100644 --- a/browser/test/Services/WindowManager/WindowManagerTests.ts +++ b/browser/test/Services/WindowManager/WindowManagerTests.ts @@ -22,18 +22,18 @@ describe("WindowManagerTests", () => { const handle1 = windowManager.createSplit("horizontal", split1) const handle2 = windowManager.createSplit("vertical", split2, split1) - assert.strictEqual(windowManager.activeSplit.id, handle2.id) + assert.strictEqual(windowManager.activeSplitHandle.id, handle2.id) handle2.close() - assert.strictEqual(windowManager.activeSplit.id, handle1.id) + assert.strictEqual(windowManager.activeSplitHandle.id, handle1.id) const handle3 = windowManager.createSplit("horizontal", split3, split1) - assert.strictEqual(windowManager.activeSplit.id, handle3.id) + assert.strictEqual(windowManager.activeSplitHandle.id, handle3.id) handle3.close() - assert.strictEqual(windowManager.activeSplit.id, handle1.id) + assert.strictEqual(windowManager.activeSplitHandle.id, handle1.id) }) it("can get split after a split is closed", async () => { diff --git a/extensions/oni-plugin-markdown-preview/package.json b/extensions/oni-plugin-markdown-preview/package.json index d27bf36027..a890d11ac3 100644 --- a/extensions/oni-plugin-markdown-preview/package.json +++ b/extensions/oni-plugin-markdown-preview/package.json @@ -21,7 +21,7 @@ } }, "tbd-dependencies": { - "marked": "^0.3.6", + "marked": "^0.4.0", "dompurify": "1.0.2", "oni-types": "^0.0.4", "oni-api": "^0.0.9" diff --git a/extensions/oni-plugin-markdown-preview/src/index.tsx b/extensions/oni-plugin-markdown-preview/src/index.tsx index 516b4efe12..cf5479ecf3 100644 --- a/extensions/oni-plugin-markdown-preview/src/index.tsx +++ b/extensions/oni-plugin-markdown-preview/src/index.tsx @@ -46,6 +46,7 @@ class MarkdownPreview extends React.PureComponent this.onBufferChanged(args)) // TODO: Subscribe "onFocusChanged" - this.subscribe(activeEditor.onBufferScrolled, args => this.onBufferScrolled(args)) + + if (this.props.oni.configuration.getValue("experimental.markdownPreview.autoScroll")) { + this.subscribe(activeEditor.onBufferScrolled, args => this.onBufferScrolled(args)) + } this.previewBuffer(activeEditor.activeBuffer) } @@ -153,6 +157,10 @@ class MarkdownPreview extends React.PureComponent this.onBufferEnter(args)) + this._oni.editors.activeEditor.onBufferLeave.subscribe(args => this.onBufferLeave(args)) } public isPaneOpen(): boolean { @@ -203,7 +213,7 @@ class MarkdownPreviewEditor implements Oni.IWindowSplit { public toggle(): void { if (this._open) { - this.close() + this.close(true) } else { this.open() } @@ -212,14 +222,19 @@ class MarkdownPreviewEditor implements Oni.IWindowSplit { public open(): void { if (!this._open) { this._open = true + this._manuallyClosed = false + const editorSplit = this._oni.windows.activeSplitHandle + // TODO: Update API this._split = this._oni.windows.createSplit("vertical", this) + editorSplit.focus() } } - public close(): void { + public close(manuallyClosed = false): void { if (this._open) { this._open = false + this._manuallyClosed = manuallyClosed this._split.close() } } @@ -229,10 +244,14 @@ class MarkdownPreviewEditor implements Oni.IWindowSplit { } private onBufferEnter(bufferInfo: Oni.EditorBufferEventArgs): void { - if (bufferInfo.language === "markdown") { + if (bufferInfo.language === "markdown" && this._manuallyClosed === false) { this.open() } } + + private onBufferLeave(bufferInfo: Oni.EditorBufferEventArgs): void { + this.close() + } } export function activate(oni: any): any { @@ -259,7 +278,7 @@ export function activate(oni: any): any { "Close Markdown Preview", "Close the Markdown preview pane if it is not already closed", () => { - preview.close() + preview.close(true) }, ), ) diff --git a/main/src/main.ts b/main/src/main.ts index 3b68a2404a..8774469fd6 100644 --- a/main/src/main.ts +++ b/main/src/main.ts @@ -242,6 +242,11 @@ export function createWindow( Log.info("...closed event completed") }) + currentWindow.webContents.on("will-navigate", (event, url) => { + event.preventDefault() + currentWindow.webContents.send("open-oni-browser", url) + }) + windows.push(currentWindow) return currentWindow diff --git a/package.json b/package.json index 6d57860b64..47e9c40b38 100644 --- a/package.json +++ b/package.json @@ -856,11 +856,11 @@ "fs-extra": "^5.0.0", "json5": "^1.0.1", "keyboard-layout": "^2.0.13", - "marked": "^0.3.6", + "marked": "^0.4.0", "minimist": "1.2.0", "msgpack-lite": "0.1.26", "ocaml-language-server": "^1.0.27", - "oni-api": "^0.0.45", + "oni-api": "^0.0.46", "oni-neovim-binaries": "0.1.2", "oni-ripgrep": "0.0.4", "oni-types": "^0.0.8", diff --git a/yarn.lock b/yarn.lock index 5029183876..15d7705b8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6537,9 +6537,9 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -marked@^0.3.6: - version "0.3.7" - resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.7.tgz#80ef3bbf1bd00d1c9cfebe42ba1b8c85da258d0d" +marked@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.4.0.tgz#9ad2c2a7a1791f10a852e0112f77b571dce10c66" math-expression-evaluator@^1.2.14: version "1.2.17" @@ -7333,17 +7333,17 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -oni-api@^0.0.45: - version "0.0.45" - resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.45.tgz#e9191fbc5069e01b3cbea644bc697be9aaf1d699" +oni-api@^0.0.46: + version "0.0.46" + resolved "https://registry.yarnpkg.com/oni-api/-/oni-api-0.0.46.tgz#99511a0c5488af1762b4744ce1ca79fea45b2b8b" oni-core-logging@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/oni-core-logging/-/oni-core-logging-1.0.0.tgz#7ad6c0ad8b06c23255202f97e229c2b0947dcf0b" -oni-neovim-binaries@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/oni-neovim-binaries/-/oni-neovim-binaries-0.1.1.tgz#7aed74c14bca2581e1447c557541192dd5e89cdd" +oni-neovim-binaries@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/oni-neovim-binaries/-/oni-neovim-binaries-0.1.2.tgz#fccfab6aa71922437119a8de149582648f4e521d" oni-release-downloader@^0.0.10: version "0.0.10" From de149d9067df46089c63ddd2b37cf2face72b55b Mon Sep 17 00:00:00 2001 From: Ryan C Date: Tue, 19 Jun 2018 21:33:03 +0100 Subject: [PATCH 096/102] Skip commits that only contain markdown changes. (#2303) --- appveyor.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 7e21b50f47..2480e4974a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,12 @@ branches: - master - /^release.*/ +# Skip CI build if the changes match these rules exactly. +# Ie, if the BACKERS.md file is changed, we don't need to build. +skip_commits: + files: + - '**/*.md' + cache: - .oni_build_cache -> package.json From 2e1f32261da719b51840777bc0f08737f2d15a1c Mon Sep 17 00:00:00 2001 From: Akin Date: Wed, 20 Jun 2018 06:43:36 +0100 Subject: [PATCH 097/102] Bugfix/inactive windows (#2335) * pull upstream * handle cases where tabstate is invalid * fix check in filter function --- browser/src/Editor/NeovimEditor/NeovimEditor.tsx | 3 +++ browser/src/neovim/NeovimWindowManager.ts | 4 +++- vim/core/oni-plugin-buffers/index.js | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx index a31f9a6f26..77a31a8053 100644 --- a/browser/src/Editor/NeovimEditor/NeovimEditor.tsx +++ b/browser/src/Editor/NeovimEditor/NeovimEditor.tsx @@ -373,6 +373,9 @@ export class NeovimEditor extends Editor implements IEditor { this.trackDisposable( this._windowManager.onWindowStateChanged.subscribe(tabPageState => { + if (!tabPageState) { + return + } const filteredTabState = tabPageState.inactiveWindows.filter(w => !!w) const inactiveIds = filteredTabState.map(w => w.windowNumber) diff --git a/browser/src/neovim/NeovimWindowManager.ts b/browser/src/neovim/NeovimWindowManager.ts index d59f89a1fd..59294eacc5 100644 --- a/browser/src/neovim/NeovimWindowManager.ts +++ b/browser/src/neovim/NeovimWindowManager.ts @@ -107,7 +107,9 @@ export class NeovimWindowManager extends Utility.Disposable { return Observable.defer(() => this._remeasure(evt)) }) .subscribe((tabState: NeovimTabPageState) => { - this._onWindowStateChangedEvent.dispatch(tabState) + if (tabState) { + this._onWindowStateChangedEvent.dispatch(tabState) + } }) } diff --git a/vim/core/oni-plugin-buffers/index.js b/vim/core/oni-plugin-buffers/index.js index f60648296e..3b26cd5674 100644 --- a/vim/core/oni-plugin-buffers/index.js +++ b/vim/core/oni-plugin-buffers/index.js @@ -16,7 +16,9 @@ const activate = Oni => { const buffers = Oni.editors.activeEditor.getBuffers() const active = Oni.editors.activeEditor.activeBuffer.filePath - const bufferMenuItems = buffers.map(b => ({ + const validBuffers = buffers.filter(b => !b.filepath) + + const bufferMenuItems = validBuffers.map(b => ({ label: `${active === b.filePath ? b.id + " %" : b.id}`, detail: truncateFilePath(b.filePath), icon: Oni.ui.getIconClassForFile(b.filePath), From 195567bdadb4f58dc924a4124947862569a27119 Mon Sep 17 00:00:00 2001 From: Kazuyuki Kamata Date: Thu, 21 Jun 2018 03:00:34 +0900 Subject: [PATCH 098/102] Fix IME with autoClosingPairs.enabled (#2338) --- browser/src/UI/Shell/ShellView.tsx | 33 ++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/browser/src/UI/Shell/ShellView.tsx b/browser/src/UI/Shell/ShellView.tsx index f698e4229a..fae4a49703 100644 --- a/browser/src/UI/Shell/ShellView.tsx +++ b/browser/src/UI/Shell/ShellView.tsx @@ -30,13 +30,30 @@ interface IShellViewComponentProps { const titleBarVisible = Platform.isMac() -export class ShellView extends React.PureComponent { +interface IShellViewState { + /** + * Tracks if composition is occurring (ie, an IME is active) + */ + isComposing: boolean +} + +export class ShellView extends React.PureComponent { + constructor(props: IShellViewComponentProps) { + super(props) + + this.state = { + isComposing: false, + } + } + public render() { return (
    this._onRootKeyDown(evt)} + onCompositionEndCapture={evt => this._onCompositionEnd(evt)} + onCompositionStartCapture={evt => this._onCompositionStart(evt)} >
    @@ -67,11 +84,23 @@ export class ShellView extends React.PureComponent private _onRootKeyDown(evt: React.KeyboardEvent): void { const vimKey = inputManager.resolvers.resolveKeyEvent(evt.nativeEvent) - if (inputManager.handleKey(vimKey)) { + if (!this.state.isComposing && inputManager.handleKey(vimKey)) { evt.stopPropagation() evt.preventDefault() } else { focusManager.enforceFocus() } } + + private _onCompositionStart(evt: React.CompositionEvent) { + this.setState({ + isComposing: true, + }) + } + + private _onCompositionEnd(evt: React.CompositionEvent) { + this.setState({ + isComposing: false, + }) + } } From d07a6154b7b6faa66306fa8d95ff2a325a17b78c Mon Sep 17 00:00:00 2001 From: Ryan C Date: Wed, 20 Jun 2018 19:23:25 +0100 Subject: [PATCH 099/102] Add hidden option for Menu bar. (#2337) --- .../Services/BrowserWindowConfigurationSynchronizer.ts | 10 +++++++--- .../src/Services/Configuration/IConfigurationValues.ts | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/src/Services/BrowserWindowConfigurationSynchronizer.ts b/browser/src/Services/BrowserWindowConfigurationSynchronizer.ts index 8ba03bcb96..9d04c03104 100644 --- a/browser/src/Services/BrowserWindowConfigurationSynchronizer.ts +++ b/browser/src/Services/BrowserWindowConfigurationSynchronizer.ts @@ -45,9 +45,13 @@ export const activate = (configuration: Configuration, colors: Colors) => { document.body.style["-webkit-font-smoothing"] = fontSmoothing } - const hideMenu: boolean = configuration.getValue("oni.hideMenu") - browserWindow.setAutoHideMenuBar(hideMenu) - browserWindow.setMenuBarVisibility(!hideMenu) + const hideMenu: boolean | "hidden" = configuration.getValue("oni.hideMenu") + if (hideMenu === "hidden") { + browserWindow.setMenu(null) + } else { + browserWindow.setAutoHideMenuBar(hideMenu) + browserWindow.setMenuBarVisibility(!hideMenu) + } const loadInit: boolean = configuration.getValue("oni.loadInitVim") if (loadInit !== loadInitVim) { diff --git a/browser/src/Services/Configuration/IConfigurationValues.ts b/browser/src/Services/Configuration/IConfigurationValues.ts index 9bc579aea1..b760f827c0 100644 --- a/browser/src/Services/Configuration/IConfigurationValues.ts +++ b/browser/src/Services/Configuration/IConfigurationValues.ts @@ -93,7 +93,8 @@ export interface IConfigurationValues { // If true, hide Menu bar by default // (can still be activated by pressing 'Alt') - "oni.hideMenu": boolean + // If hidden, menu bar is hidden entirely. + "oni.hideMenu": boolean | "hidden" // glob pattern of files to exclude from fuzzy finder (Ctrl-P) "oni.exclude": string[] From 7ebdfea52bd270707052b997d574715fdd589c2c Mon Sep 17 00:00:00 2001 From: Jeffrey Tao Date: Thu, 21 Jun 2018 11:21:56 -0700 Subject: [PATCH 100/102] Show full modifier key names in KeyDisplayer (#2340) * Unified key chord descriptions between KeyBindingInfo and KeyDisplayer * Moved chord parsing to KeyParser * Lint fixes * parseChordParts tests * key prop for KeyBindingInfo * The linter, again --- browser/src/Input/KeyParser.ts | 34 ++++++++++++++ browser/src/Services/InputManager.ts | 6 --- .../Services/KeyDisplayer/KeyDisplayer.tsx | 3 +- .../KeyDisplayer/KeyDisplayerView.tsx | 13 +----- browser/src/UI/components/KeyBindingInfo.tsx | 45 +++++-------------- browser/test/Input/KeyParserTests.ts | 22 +++++++++ 6 files changed, 71 insertions(+), 52 deletions(-) diff --git a/browser/src/Input/KeyParser.ts b/browser/src/Input/KeyParser.ts index ba8c882200..b6daed71bf 100644 --- a/browser/src/Input/KeyParser.ts +++ b/browser/src/Input/KeyParser.ts @@ -84,3 +84,37 @@ export const parseKey = (key: string): IKey => { meta: hasMeta, } } + +// Parse a chord string (e.g. ) into textual descriptions of the relevant keys +// -> ["control", "shift", "p"] +export const parseChordParts = (keys: string): string[] => { + const parsedKeys = parseKeysFromVimString(keys) + + if (!parsedKeys || !parsedKeys.chord || parsedKeys.chord.length === 0) { + return null + } + + const firstChord = parsedKeys.chord[0] + + const chordParts: string[] = [] + + if (firstChord.meta) { + chordParts.push("meta") + } + + if (firstChord.control) { + chordParts.push("control") + } + + if (firstChord.alt) { + chordParts.push("alt") + } + + if (firstChord.shift) { + chordParts.push("shift") + } + + chordParts.push(firstChord.character) + + return chordParts +} diff --git a/browser/src/Services/InputManager.ts b/browser/src/Services/InputManager.ts index beedd8da39..b6c1b16160 100644 --- a/browser/src/Services/InputManager.ts +++ b/browser/src/Services/InputManager.ts @@ -8,8 +8,6 @@ export type ActionOrCommand = string | ActionFunction export type FilterFunction = () => boolean -import { IKeyChord, parseKeysFromVimString } from "./../Input/KeyParser" - export interface KeyBinding { action: ActionOrCommand filter?: FilterFunction @@ -141,10 +139,6 @@ export class InputManager implements Oni.Input.InputManager { ) } - public parseKeys(keys: string): IKeyChord { - return parseKeysFromVimString(keys) - } - /** * Internal Methods */ diff --git a/browser/src/Services/KeyDisplayer/KeyDisplayer.tsx b/browser/src/Services/KeyDisplayer/KeyDisplayer.tsx index d08b55e799..65d5f1b5c9 100644 --- a/browser/src/Services/KeyDisplayer/KeyDisplayer.tsx +++ b/browser/src/Services/KeyDisplayer/KeyDisplayer.tsx @@ -10,6 +10,7 @@ import { Store } from "redux" import { IDisposable } from "oni-types" +import { parseChordParts } from "./../../Input/KeyParser" import { Configuration } from "./../Configuration" import { EditorManager } from "./../EditorManager" import { InputManager } from "./../InputManager" @@ -53,7 +54,7 @@ export class KeyDisplayer { this._store.dispatch({ type: "ADD_KEY", - key: resolution, + key: parseChordParts(resolution).join("+"), timeInMilliseconds: new Date().getTime(), }) diff --git a/browser/src/Services/KeyDisplayer/KeyDisplayerView.tsx b/browser/src/Services/KeyDisplayer/KeyDisplayerView.tsx index 9db13aac8c..71c4e1b58d 100644 --- a/browser/src/Services/KeyDisplayer/KeyDisplayerView.tsx +++ b/browser/src/Services/KeyDisplayer/KeyDisplayerView.tsx @@ -29,22 +29,11 @@ export interface IKeyDisplayerViewProps { groupedKeys: IKeyPressInfo[][] } -const getStringForKey = (key: string) => { - if (key === "") { - return " " - } - - return key -} - export class KeyDisplayerView extends React.PureComponent { public render(): JSX.Element { const keyElements = this.props.groupedKeys.map((k, idx) => ( - {k.reduce( - (prev: string, cur: IKeyPressInfo) => prev + getStringForKey(cur.key), - "", - )} + {k.map(keyPress => keyPress.key).join(" ")} )) diff --git a/browser/src/UI/components/KeyBindingInfo.tsx b/browser/src/UI/components/KeyBindingInfo.tsx index 7552f87b0f..ad47b6c5bf 100644 --- a/browser/src/UI/components/KeyBindingInfo.tsx +++ b/browser/src/UI/components/KeyBindingInfo.tsx @@ -8,6 +8,7 @@ import styled from "styled-components" import * as React from "react" +import { parseChordParts } from "./../../Input/KeyParser" import { inputManager } from "./../../Services/InputManager" export interface IKeyBindingInfoProps { @@ -31,39 +32,17 @@ export class KeyBindingInfo extends React.PureComponent{"meta"}) - elems.push({"+"}) - } - - if (firstChord.control) { - elems.push({"control"}) - elems.push({"+"}) - } - - if (firstChord.alt) { - elems.push({"alt"}) - elems.push({"+"}) - } - - if (firstChord.shift) { - elems.push({"shift"}) - elems.push({"+"}) - } - - elems.push({firstChord.character}) - - return {elems} + // 1. Get the key(s) in the chord binding + // 2. Intersperse with "+" + // 3. Create KeyWrappers for each segment + return ( + + {parseChordParts(boundKeys[0]) + .reduce((acc, chordKey) => acc.concat(chordKey, "+"), []) + .slice(0, -1) + .map((chordPart, index) => {chordPart})} + + ) } } diff --git a/browser/test/Input/KeyParserTests.ts b/browser/test/Input/KeyParserTests.ts index 0a98be824e..2377db0e03 100644 --- a/browser/test/Input/KeyParserTests.ts +++ b/browser/test/Input/KeyParserTests.ts @@ -31,4 +31,26 @@ describe("KeyParser", () => { ]) }) }) + + describe("parseChordParts", () => { + it("parses modifier keys", () => { + const tests: Array<[string, string[]]> = [ + ["a", ["a"]], + ["", ["control", "a"]], + ["", ["meta", "a"]], + ["", ["alt", "a"]], + ["", ["shift", "a"]], + ["", ["meta", "control", "alt", "shift", "a"]], + ] + + tests.forEach(test => { + assert.deepEqual(KeyParser.parseChordParts(test[0]), test[1]) + }) + }) + + it("ignores keys beyond the first chord", () => { + const result = KeyParser.parseChordParts("b") + assert.deepEqual(result, ["control", "a"]) + }) + }) }) From 633d838ecc3e5b1a6dccde236d9761d047546ec0 Mon Sep 17 00:00:00 2001 From: Kazuyuki Kamata Date: Fri, 22 Jun 2018 03:28:11 +0900 Subject: [PATCH 101/102] Fix a first character problem in IME with autoClosingPairs.enabled (#2341) --- browser/src/UI/Shell/ShellView.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/browser/src/UI/Shell/ShellView.tsx b/browser/src/UI/Shell/ShellView.tsx index fae4a49703..0c7fda57fb 100644 --- a/browser/src/UI/Shell/ShellView.tsx +++ b/browser/src/UI/Shell/ShellView.tsx @@ -83,8 +83,16 @@ export class ShellView extends React.PureComponent): void { + // onCompositionStart can't detect composing mode for the first character + // because it is fired after onKeyDown. + // keyCode is deprecated but it seems this is the only method to detect + // composing mode for the first character for now. + let isComposing = false + if (evt.keyCode === 229) { + isComposing = true + } const vimKey = inputManager.resolvers.resolveKeyEvent(evt.nativeEvent) - if (!this.state.isComposing && inputManager.handleKey(vimKey)) { + if (!this.state.isComposing && !isComposing && inputManager.handleKey(vimKey)) { evt.stopPropagation() evt.preventDefault() } else { From 82faff6a9bcb618fdc2f0b434b93fc8a362586f3 Mon Sep 17 00:00:00 2001 From: keforbes Date: Thu, 21 Jun 2018 13:19:32 -0600 Subject: [PATCH 102/102] create Text Objects tutorial (#2319) * add files * fix a word * add some more text object examples --- .../src/Services/Learning/Tutorial/Notes.tsx | 18 ++ .../Tutorials/TextObjectsTutorial.tsx | 231 ++++++++++++++++++ .../Learning/Tutorial/Tutorials/index.tsx | 2 + 3 files changed, 251 insertions(+) create mode 100644 browser/src/Services/Learning/Tutorial/Tutorials/TextObjectsTutorial.tsx diff --git a/browser/src/Services/Learning/Tutorial/Notes.tsx b/browser/src/Services/Learning/Tutorial/Notes.tsx index f32d0fbca2..f01917b0b5 100644 --- a/browser/src/Services/Learning/Tutorial/Notes.tsx +++ b/browser/src/Services/Learning/Tutorial/Notes.tsx @@ -508,3 +508,21 @@ export const RepeatOppositeKey = (): JSX.Element => { /> ) } + +export const innerTextObjectKey = (): JSX.Element => { + return ( + Select a Text Object within delimiter characters} + /> + ) +} + +export const aTextObjectKey = (): JSX.Element => { + return ( + Select a Text Object and its delimiter characters} + /> + ) +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/TextObjectsTutorial.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/TextObjectsTutorial.tsx new file mode 100644 index 0000000000..8432bf390a --- /dev/null +++ b/browser/src/Services/Learning/Tutorial/Tutorials/TextObjectsTutorial.tsx @@ -0,0 +1,231 @@ +/** + * TextObjectsTutorial.tsx + * + * Tutorial to teach text objects + */ + +import * as React from "react" + +import { ITutorial, ITutorialMetadata, ITutorialStage } from "./../ITutorial" +import * as Notes from "./../Notes" +import * as Stages from "./../Stages" + +const stage1line1 = "Text objects typically have delimiters like ( ) and { }" +const stage1line2 = "This sentence has a phrase (within some parentheses) for testing" +const stage1line2a = "This sentence has a phrase () for testing" +const stage1line2b = "This sentence has a phrase for testing" +const stage1line2c = + "This sentence has a phrase (within some parentheses) for testingwithin some parentheses" +const stage1line2d = "This sentence has a phrase (this is a test) for testing" + +const stage2line1 = "Text objects can also span multiple lines" +const stage2line2 = "{" +const stage2line3 = " these are mostly useful" +const stage2line4 = " when editing code" +const stage2line5 = "}" + +const stage3line1 = + "Text Objects aren't limited to single character delimiters; they also work with HTML!" +const stage3line2 = "" +const stage3line3 = "

    " +const stage3line4 = " here is some text" +const stage3line4a = " " +const stage3line4b = " " +const stage3line5 = "

    " +const stage3line5a = " " +const stage3line6 = "" + +const stage4line1 = "There are many other Text Objects we can manipulate" +const stage4line2 = "[ there ] ( are ) { many } < text > objects ' to ' \" try \"" +const stage4line2a = "[] ( are ) { many } < text > objects ' to ' \" try \"" +const stage4line2b = "[] () { many } < text > objects ' to ' \" try \"" +const stage4line2c = "[] () {} < text > objects ' to ' \" try \"" +const stage4line2d = "[] () {} <> objects ' to ' \" try \"" +const stage4line2e = "[] () {} <> ' to ' \" try \"" +const stage4line2f = "[] () {} <> '' \" try \"" +const stage4line2g = "[] () {} <> '' \"\"" + +export class TextObjectsTutorial implements ITutorial { + private _stages: ITutorialStage[] + + constructor() { + this._stages = [ + new Stages.SetBufferStage([stage1line1, stage1line2]), + new Stages.MoveToGoalStage("Move inside the parentheses", 1, 41), + Stages.combine( + "Use 'di(' to delete everything within the parentheses", + new Stages.DeleteCharactersStage(null, 1, 28, "within some parentheses"), + new Stages.WaitForStateStage(null, [stage1line1, stage1line2a]), + ), + new Stages.WaitForStateStage("Notice it left the parentheses. Hit 'u' to undo", [ + stage1line1, + stage1line2, + ]), + new Stages.MoveToGoalStage("Move inside the parentheses", 1, 41), + Stages.combine( + "Use 'da(' to delete the parentheses and everything within", + new Stages.DeleteCharactersStage(null, 1, 27, "(within some parentheses)"), + new Stages.WaitForStateStage(null, [stage1line1, stage1line2b]), + ), + new Stages.WaitForStateStage("Hit 'u' to undo", [stage1line1, stage1line2]), + new Stages.MoveToGoalStage("Move inside the parentheses", 1, 41), + new Stages.WaitForRegisterStage( + "Use 'yi(' to yank everything with the parentheses", + "within some parentheses", + ), + new Stages.MoveToGoalStage("Move to the end of the line", 1, 63), + new Stages.WaitForStateStage("Paste from the clipboard with 'p'", [ + stage1line1, + stage1line2c, + ]), + new Stages.WaitForStateStage("Hit 'u' to undo", [stage1line1, stage1line2]), + new Stages.MoveToGoalStage("Move inside the parentheses", 1, 41), + Stages.combine( + "Use 'ci(' to change everything within the parentheses", + new Stages.WaitForModeStage(null, "insert"), + new Stages.WaitForStateStage(null, [stage1line1, stage1line2a]), + ), + new Stages.WaitForStateStage("Type 'this is a test'", [stage1line1, stage1line2d]), + new Stages.WaitForModeStage("Hit to exit insert mode", "normal"), + + new Stages.SetBufferStage([ + stage2line1, + stage2line2, + stage2line3, + stage2line4, + stage2line5, + ]), + new Stages.MoveToGoalStage("Move inside the curly brackets", 2, 14), + Stages.combine( + "Use 'vi{' to select everything within the curly brackets", + new Stages.MoveToGoalStage(null, 3, 19), + new Stages.WaitForModeStage(null, "visual"), + ), + new Stages.WaitForModeStage("Hit to exit visual mode", "normal"), + Stages.combine( + "Use 'va{' to select everything including the curly brackets", + new Stages.MoveToGoalStage(null, 4, 0), + new Stages.WaitForModeStage(null, "visual"), + ), + new Stages.WaitForModeStage("Hit to exit visual mode", "normal"), + + new Stages.SetBufferStage([ + stage3line1, + stage3line2, + stage3line3, + stage3line4, + stage3line5, + stage3line6, + ]), + new Stages.MoveToGoalStage("Move inside the HTML tag", 3, 20), + Stages.combine( + "Use 'dit' to delete everything within the tag", + new Stages.DeleteCharactersStage(null, 3, 12, "here is some text"), + new Stages.WaitForStateStage(null, [ + stage3line1, + stage3line2, + stage3line3, + stage3line4a, + stage3line5, + stage3line6, + ]), + ), + new Stages.WaitForStateStage("Hit 'u' to undo", [ + stage3line1, + stage3line2, + stage3line3, + stage3line4, + stage3line5, + stage3line6, + ]), + Stages.combine( + "Use 'dat' to delete the tag and its contents", + new Stages.DeleteCharactersStage(null, 3, 6, "here is some text"), + new Stages.WaitForStateStage(null, [ + stage3line1, + stage3line2, + stage3line3, + stage3line4b, + stage3line5, + stage3line6, + ]), + ), + Stages.combine( + "Use 'dat' to delete the

    tag and its contents", + new Stages.DeleteCharactersStage(null, 2, 3, "

    "), + new Stages.DeleteCharactersStage(null, 3, 0, " "), + new Stages.DeleteCharactersStage(null, 4, 0, "

    "), + new Stages.WaitForStateStage(null, [ + stage3line1, + stage3line2, + stage3line5a, + stage3line6, + ]), + ), + + new Stages.SetBufferStage([stage4line1, stage4line2]), + new Stages.MoveToGoalStage("Move to the next line", 1, 0), + Stages.combine( + "Try 'di['", + new Stages.DeleteCharactersStage(null, 1, 1, " there "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2a]), + ), + Stages.combine( + "Try 'di('", + new Stages.DeleteCharactersStage(null, 1, 4, " are "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2b]), + ), + Stages.combine( + "Try 'di{'", + new Stages.DeleteCharactersStage(null, 1, 7, " many "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2c]), + ), + Stages.combine( + "Try 'di<'", + new Stages.DeleteCharactersStage(null, 1, 10, " text "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2d]), + ), + new Stages.MoveToGoalStage("Move inside the word 'objects'", 1, 15), + Stages.combine( + "Try 'diw'", + new Stages.DeleteCharactersStage(null, 1, 12, "objects"), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2e]), + ), + Stages.combine( + "Try di'", + new Stages.DeleteCharactersStage(null, 1, 14, " to "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2f]), + ), + Stages.combine( + 'Try di"', + new Stages.DeleteCharactersStage(null, 1, 17, " try "), + new Stages.WaitForStateStage(null, [stage4line1, stage4line2g]), + ), + ] + } + + public get metadata(): ITutorialMetadata { + return { + id: "oni.tutorial.text_objects", + name: "Text Objects: i, a", + description: + 'Everything you\'ve learned so far has involved motions from the current cursor position to a destination of some kind. Now it\'s time to learn about Text Objects, blocks of text with their own starting and ending characters. Text Objects can be manipulated with the operators you\'ve already learned such as `y`, `c`, `d`, and `v`. In general, defining a text obect with `i` will be the "inner" object, ignoring the text object boundary characters. Defining a text object with `a` will be "an" object, including the text object boundaries.', + level: 240, + } + } + + public get stages(): ITutorialStage[] { + return this._stages + } + + public get notes(): JSX.Element[] { + return [ + , + , + , + , + , + , + ] + } +} diff --git a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx index 1763a478e9..66ad29f9aa 100644 --- a/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx +++ b/browser/src/Services/Learning/Tutorial/Tutorials/index.tsx @@ -15,6 +15,7 @@ import { InsertAndUndoTutorial } from "./InsertAndUndoTutorial" import { SearchInBufferTutorial } from "./SearchInBufferTutorial" import { SwitchModeTutorial } from "./SwitchModeTutorial" import { TargetsVimPluginTutorial } from "./TargetsVimPluginTutorial" +import { TextObjectsTutorial } from "./TextObjectsTutorial" import { VerticalMovementTutorial } from "./VerticalMovementTutorial" import { VisualModeTutorial } from "./VisualModeTutorial" import { WordMotionTutorial } from "./WordMotionTutorial" @@ -37,4 +38,5 @@ export const AllTutorials: ITutorial[] = [ new VisualModeTutorial(), new TargetsVimPluginTutorial(), new InlineFindingTutorial(), + new TextObjectsTutorial(), ]