Skip to content

Commit

Permalink
feat(exthost): Additional status bar item functionality (onivim#1793)
Browse files Browse the repository at this point in the history
* Start wiring additional functionality, like labels, up

* Get label parsing green

* WIP: Label parsing / tests for status bar

* Hook up label

* Use Label for status bar rendering

* Wire up commands in status bar UI

* Make links clickable

* Additional status bar capabilities
  • Loading branch information
bryphe authored May 20, 2020
1 parent 8c600e0 commit 66ca670
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 18 deletions.
6 changes: 6 additions & 0 deletions development_extensions/oni-dev-extension/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ function activate(context) {
}
// Create a simple status bar
let item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1000);
item.color = new vscode.ThemeColor("foreground");
item.command = "developer.oni.statusBarClicked";
item.text = "Developer";
item.show();

let cleanup = (disposable) => context.subscriptions.push(disposable);

cleanup(vscode.commands.registerCommand('developer.oni.statusBarClicked', () => {
vscode.window.showWarningMessage('You clicked developer');
}));

cleanup(vscode.languages.registerDefinitionProvider('oni-dev', {
provideDefinition: (document, _position, _token) => {
Expand Down
22 changes: 22 additions & 0 deletions src/Exthost/Command.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
open Oni_Core;

[@deriving show]
type t = {
id: string,
title: option(string),
};

module Decode = {
let decode = {
Json.Decode.(
obj(({field, _}) =>
{
id: field.required("id", string),
title: field.optional("title", string),
}
)
);
};
};

let decode = Decode.decode;
2 changes: 2 additions & 0 deletions src/Exthost/Exthost.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Extension = Exthost_Extension;
module Protocol = Exthost_Protocol;
module Transport = Exthost_Transport;

module Command = Command;
module CompletionContext = CompletionContext;
module CompletionKind = CompletionKind;
module DefinitionLink = DefinitionLink;
Expand All @@ -14,6 +15,7 @@ module DocumentHighlight = DocumentHighlight;
module DocumentSelector = DocumentSelector;
module DocumentSymbol = DocumentSymbol;
module Eol = Eol;
module Label = Label;
module Location = Location;
module ModelAddedDelta = ModelAddedDelta;
module ModelChangedEvent = ModelChangedEvent;
Expand Down
30 changes: 28 additions & 2 deletions src/Exthost/Exthost.rei
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ module Extension = Exthost_Extension;
module Protocol = Exthost_Protocol;
module Transport = Exthost_Transport;

module Command: {
[@deriving show]
type t = {
id: string,
title: option(string),
};

let decode: Json.decoder(t);
};

module CompletionContext: {
type triggerKind =
| Invoke
Expand Down Expand Up @@ -147,6 +157,20 @@ module ReferenceContext: {
let encode: Json.encoder(t);
};

module Label: {
[@deriving show]
type segment =
| Text(string)
| Icon(string);

[@deriving show]
type t = list(segment);

let of_string: string => t;

let decode: Json.decoder(t);
};

module SCM: {
[@deriving show({with_path: false})]
type command = {
Expand Down Expand Up @@ -654,11 +678,13 @@ module Msg: {
type msg =
| SetEntry({
id: string,
text: string,
label: Label.t,
source: string,
alignment,
command: option(Command.t),
priority: int,
});
})
| Dispose({id: int});
};

[@deriving show]
Expand Down
63 changes: 63 additions & 0 deletions src/Exthost/Label.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
open Oni_Core;

[@deriving show]
type segment =
| Text(string)
| Icon(string);

[@deriving show]
type t = list(segment);

module Parse = {
open Oniguruma;
let iconMatcher = OnigRegExp.create("\\$\\((.*)\\)") |> Result.get_ok;

let rec loop = str => {
let len = String.length(str);
let matches = OnigRegExp.search(str, 0, iconMatcher);

if (Array.length(matches) == 0) {
[Text(str)];
} else {
let matchPos = matches[0].startPos;
let matchEnd = matches[0].endPos;

if (matchPos > 0) {
[
Text(String.sub(str, 0, matchPos)),
...loop(String.sub(str, matchPos, len - matchPos)),
];
} else {
let text = OnigRegExp.Match.getText(matches[1]);
if (matchEnd == len) {
[Icon(text)];
} else {
[Icon(text), ...loop(String.sub(str, matchEnd, len - matchEnd))];
};
};
};
};

let parse = str => {
let len = String.length(str);

if (len == 0) {
[];
} else if (len == 1) {
[Text(str)];
} else if (str.[0] == '"' && str.[len - 1] == '"') {
let text = String.sub(str, 1, len - 2);
loop(text);
} else {
[Text(str)];
};
};
};

module Decode = {
open Json.Decode;
let decode: Json.decoder(t) = string |> map(Parse.parse);
};

let decode = Decode.decode;
let of_string = Parse.parse;
32 changes: 26 additions & 6 deletions src/Exthost/Msg.re
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module ExtCommand = Command;
open Oni_Core;
open Oni_Core.Utility;

Expand Down Expand Up @@ -480,11 +481,23 @@ module StatusBar = {
type msg =
| SetEntry({
id: string,
text: string,
label: Label.t,
source: string,
alignment,
command: option(ExtCommand.t),
priority: int,
});
})
| Dispose({id: int});
let parseCommand = commandJson =>
switch (commandJson) {
| `String(jsonString) =>
jsonString
|> Yojson.Safe.from_string
|> Json.Decode.decode_value(Json.Decode.nullable(ExtCommand.decode))
|> Result.map_error(Json.Decode.string_of_error)
| _ => Ok(None)
};
let handle = (method, args: Yojson.Safe.t) => {
switch (method, args) {
Expand All @@ -494,17 +507,24 @@ module StatusBar = {
`String(id),
_,
`String(source),
`String(text),
_,
_,
labelJson,
_tooltip,
commandJson,
_,
`String(alignment),
`String(priority),
]),
) =>
open Base.Result.Let_syntax;
let alignment = stringToAlignment(alignment);
let priority = int_of_string_opt(priority) |> Option.value(~default=0);
Ok(SetEntry({id, source, text, alignment, priority}));
let%bind command = parseCommand(commandJson);
let%bind label =
labelJson
|> Json.Decode.decode_value(Label.decode)
|> Result.map_error(Json.Decode.string_of_error);
Ok(SetEntry({id, source, label, alignment, priority, command}));
| ("$dispose", `List([`Int(id)])) => Ok(Dispose({id: id}))
| _ =>
Error(
"Unable to parse method: "
Expand Down
14 changes: 10 additions & 4 deletions src/Model/StatusBarModel.re
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,27 @@ type action =
| DiagnosticsClicked
| NotificationClearAllClicked
| NotificationCountClicked
| NotificationsContextMenu;
| NotificationsContextMenu
| ContributedItemClicked({
id: string,
command: string,
});

module Item = {
type t = {
id: string,
priority: int,
text: string,
label: Exthost.Label.t,
alignment: Exthost.Msg.StatusBar.alignment,
command: option(string),
};

let create = (~id, ~priority, ~text, ~alignment=Left, ()) => {
let create = (~command=?, ~id, ~priority, ~label, ~alignment=Left, ()) => {
id,
priority,
text,
label,
alignment,
command,
};
};

Expand Down
13 changes: 11 additions & 2 deletions src/Store/ExtensionClient.re
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,19 @@ let create = (~config, ~extensions, ~setup: Setup.t) => {
dispatch(ExtMessageReceived({severity, message, extensionId}));
Lwt.return(Reply.okEmpty);

| StatusBar(SetEntry({id, text, alignment, priority, _})) =>
| StatusBar(SetEntry({id, label, alignment, priority, command, _})) =>
let command =
command |> Option.map(({id, _}: Exthost.Command.t) => id);
dispatch(
Actions.StatusBarAddItem(
StatusBarModel.Item.create(~id, ~text, ~alignment, ~priority, ()),
StatusBarModel.Item.create(
~command?,
~id,
~label,
~alignment,
~priority,
(),
),
),
);
Lwt.return(Reply.okEmpty);
Expand Down
5 changes: 5 additions & 0 deletions src/Store/ExtensionClientStoreConnector.re
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ let start = (extensions, extHostClient: Exthost.Client.t) => {
executeContributedCommandEffect(command, arguments),
)

| StatusBar(ContributedItemClicked({command, _})) => (
state,
executeContributedCommandEffect(command, []),
)

| VimDirectoryChanged(path) => (state, changeWorkspaceEffect(path))

| BufferEnter({id, version, filePath, fileType, _}) =>
Expand Down
49 changes: 49 additions & 0 deletions src/UI/Label.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Label.re
*
* Container to help render Exthost.Label.t
*/

open Revery;
open Revery.UI;
open Oni_Core;
open Oni_Components;

module Styles = {
open Style;
let text = (~fontSize=11., ~color, uiFont: UiFont.t) => [
fontFamily(uiFont.fontFile),
Style.fontSize(fontSize),
textWrap(TextWrapping.NoWrap),
Style.color(color),
];
};

let textToElement = (~color, ~font, ~text) => {
<Text style={Styles.text(~color, font)} text />;
};

let iconNameToCharacter =
fun
| "alert" => Some(FontAwesome.exclamationTriangle)
| _ => None;

let iconToElement = (~color, icon) => {
<View style=Style.[margin(4)]> <FontIcon icon color /> </View>;
};

let make = (~font, ~color, ~label: Exthost.Label.t, ()) => {
Exthost.Label.(
label
|> List.map(
fun
| Text(text) => textToElement(~color, ~font, ~text)
| Icon(iconName) =>
iconName
|> iconNameToCharacter
|> Option.map(iconToElement(~color))
|> Option.value(~default=React.empty),
)
|> React.listToElement
);
};
24 changes: 22 additions & 2 deletions src/UI/StatusBar.re
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,28 @@ let%component make =
let%hook (yOffset, _animationState, _reset) =
Hooks.animation(transitionAnimation);

let toStatusBarElement = (statusBarItem: Item.t) =>
<textItem font background theme text={statusBarItem.text} />;
let toStatusBarElement = (statusItem: Item.t) => {
let onClick =
statusItem.command
|> Option.map((command, ()) =>
GlobalContext.current().dispatch(
Actions.StatusBar(
ContributedItemClicked({id: statusItem.id, command}),
),
)
);

<item ?onClick>
<View
style=Style.[
flexDirection(`Row),
justifyContent(`Center),
alignItems(`Center),
]>
<Label font color=Revery.Colors.white label={statusItem.label} />
</View>
</item>;
};

let leftItems =
state.statusBar
Expand Down
Loading

0 comments on commit 66ca670

Please sign in to comment.