How do you access highlighted text in Purescript? - purescript

I'm creating an application in Purescript and I want to have a text box displaying some documentation, and then I want to perform some NLP tasks on the server based on the sentences highlighted by the mouse on that text.
How could I extract that text in Purescript?

You can subscribe to the selectionchange event in the initialize handler of your halogen component.
Init -> do
doc <- H.liftEffect $ Web.window >>= Window.document
void $ H.subscribe $
ES.eventListenerEventSource (EventType "selectionchange") (Document.toEventTarget doc)
(const $ Just OnSelectionChange)
Then write an FFI function to get current selected text, something like
foreign import getSelectionString :: Effect String
-- js
const getSelectionString = () => window.getSelection().toString()
Then use getSelectionString in the OnSelectionChange handler.
A not exactly the same example here
https://github.com/nonbili/halogen-contenteditable-example/blob/master/src/Example/Editor.purs#L178-L180

Related

How to search for and highlight a substring in Codemirror 6?

I'm building a simple code editor to help children learn HTML. One feature I'm trying to add is that when users mouseover their rendered code (in an iframe), the corresponding HTML code in the editor is highlighted. So, for example, if a user mouses-over an image of kittens, the actual code, , would be highlighted in the editor.
Mousing-over the iframe to get the html source for that element is the easy part, which I've done (using document.elementFromPoint(e.clientX, e.clientY in the iframe itself, and posting that up to the parent) - so that's not the part I need help with. The part I can't figure out is how to search for and highlight that string of selected code in the code editor.
I'm using Codemirror 6 for this project, as it seems as it will give me the most flexibility to create such a feature. However, as a Codemirror 6 novice, I'm struggling with the documentation to find out where I should start. It seems like the steps I need to complete to accomplish this are:
Search for a range in the editor's text that matches a string (ie.'<img src="kittens.gif"').
Highlight that range in the editor.
Can anyone out there give me some advice as to where in the Codemirror 6 API I should look to start implementing this? It seems like it should be easy, but my unfamiliarity with the Codemirror API and the terse documentation is making this difficult.
1. Search for a range in the editor's text that matches a string (ie.'<img src="kittens.gif"').
You can use SearchCursor class (iterator) to get the character's range where is located the DOM element in your editor.
// the import for SearchCursor class
import {SearchCursor} from "#codemirror/search"
// your editor's view
let main_view = new EditorView({ /* your code */ });
// will create a cursor based on the doc content and the DOM element as a string (outerHTML)
let cursor = new SearchCursor(main_view.state.doc, element.outerHTML);
// will search the first match of the string element.outerHTML in the editor view main_view.state.doc
cursor.next()
// display the range where is located your DOM element in your editor
console.log(cursor.value);
2. Highlight that range in the editor.
As described in the migration documentation here, marked text is replace by decoration. To highlight a range in the editor with codemirror 6, you need to create one decoration and apply it in a dispatch on your view. This decoration need to be provide by an extension that you add in the extensions of your editor view.
// the import for the 3 new classes
import {StateEffect, StateField} from "#codemirror/state"
import {Decoration} from "#codemirror/view"
// code mirror effect that you will use to define the effect you want (the decoration)
const highlight_effect = StateEffect.define();
// define a new field that will be attached to your view state as an extension, update will be called at each editor's change
const highlight_extension = StateField.define({
create() { return Decoration.none },
update(value, transaction) {
value = value.map(transaction.changes)
for (let effect of transaction.effects) {
if (effect.is(highlight_effect)) value = value.update({add: effect.value, sort: true})
}
return value
},
provide: f => EditorView.decorations.from(f)
});
// this is your decoration where you can define the change you want : a css class or directly css attributes
const highlight_decoration = Decoration.mark({
// attributes: {style: "background-color: red"}
class: 'red_back'
});
// your editor's view
let main_view = new EditorView({
extensions: [highlight_extension]
});
// this is where the change takes effect by the dispatch. The of method instanciate the effect. You need to put this code where you want the change to take place
main_view.dispatch({
effects: highlight_effect.of([highlight_decoration.range(cursor.value.from, cursor.value.to)])
});
Hope it will help you to implement what you want ;)
Have a look at #codemirror/search.
Specifically, the source code implementation of Selection Matching may be of use for you to adapt.
It uses Decoration.mark over a range of text.
You can use SearchCursor to iterate over ranges that match your pattern (or RegExpCursor)
Use getSearchCursor, something like this:
var cursor = cmEditor.getSearchCursor(keyword , CodeMirror.Pos(cmEditor.firstLine(), 0), {caseFold: true, multiline: true});
if(cursor.find(false)){ //move to that position.
cmEditor.setSelection(cursor.from(), cursor.to());
cmEditor.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
}
Programmatically search and select a keyword
Take a look at getSearchCursor source code it it give some glow about how it works and its usage.
So use getSearchCursor for finding text and optionally use markText for highlighting text because you can mark text with setSelection method of editor.
Selection Marking Demo
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
styleSelectedText: true
});
editor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: "styled-background"});
And it seem this is what you are looking for:
codemirror: search and highlight multipule words without dialog
RegExpCursor is another option that you can use:
new RegExpCursor(
text: Text,
query: string,
options⁠?: {ignoreCase⁠?: boolean},
from⁠?: number = 0,
to⁠?: number = text.length
)
Sample usage at:
Replacing text between dollar signs for Mathml expression.

React-Bootstap-Typeahead: Manually set custom display value in onChange() upon menu selection

In the onChange of React-Bootstrap-Typeahead, I need to manually set a custom display value. My first thought was to use a ref and do something similar to the .clear() in this example.
But although .clear() works, inputNode.value = 'abc' does not work, and I'm left with the old selected value from the menu.
onChange={option => {
typeaheadRef.current.blur(); // This works
typeaheadRef.current.inputNode.value = 'abc'; // This does not work (old value is retained)
}}
I also tried directly accessing the DOM input element, whose ID I know, and doing
var inputElement = document.querySelector('input[id=myTypeahead]');
inputElement.value = 'abc';
But that didn't work either. For a brief second, right after my changed value = , I do see the new display label, but then it's quickly lost. I think the component saves or retains the menu-selected value.
Note: I cannot use selected, I use defaultSelected. I have some Formik-related behavior that I've introduced, and it didn't work with selected, so I'm stuck with defaultSelected.
The only workaround I found is to re-render the Typeahead component (hide and re-show, from a blank state) with a new defaultSelected="abc" which is a one-time Mount-time value specification for the control.
I couldn't get selected=.. to work, I have a wrapper around the component which makes it fit into Formik with custom onChange and onInputChange and selected wasn't working with that.
So the simple workaround that works is, if the visibility of the Typeahead depends on some condition (otherwise it won't be rendered), use that to momentarily hide and re-show the component (a brand new repaint) with a new defaultSelected, e.g.
/* Conditions controlling the visibility of the Typeahead */
!isEmptyObject(values) &&
(values.approverId === null || (values.approverId !== null && detailedApproverUserInfo)
)
&&
<AsyncTypehead defaultSelected={{...whatever is needed to build the string, or the literal string itself...}}
..
// Given the above visibility condition, we'll hide/re-show the component
// The below will first hide the control in React's renders
setFieldValue("approver", someId);
setDetailedUserInfo(null);
// The below will re-show the control in React's renders, after a small delay (a fetch)
setDetailedUserInfo(fetchDetailedUserInfo());

How can I dynamically create HTML components in Marko?

I want to create new Marko components every time the user clicks a button — by calling something like the JavaScript DOM method document.createElement("tag"). How can I do this in Marko, not just with ordinary HTML tags, but with custom Marko tags?
What I tried: document.createElement("custom-marko-component")
Expected behavior: Marko engine compiles a new instance of the custom component.
Actual behavior: The browser makes a useless new <custom-marko-component></custom-marko-component>.
Use Marko's rendering functions (documentation: https://markojs.com/docs/rendering/):
Example:
// Create the custom component, like document.createElement() but asynchronous.
// Import `./custom-marko-component.marko`
var customComponent = require("./custom-marko-component");
var resultPromise = customComponent.render({});
// Insert the custom component into the webpage.
resultPromise.then(result => {
result.appendTo(document.body);
});

Eclipse: selection autocopy to clipboard

I love an Emacs feature to copy selection to clipboard automatically. Is it possible to do the same on Eclipse?
Environment: Windows XP, Helios
To copy a String from Eclipse to the clipboard, you can use
void copyToClipboard (String toClipboard, Display display){
String toClipboard = "my String";
Clipboard clipboard = new Clipboard(display);
TextTransfer [] textTransfer = {TextTransfer.getInstance()};
clipboard.setContents(new Object [] {toClipboard}, textTransfer);
clipboard.dispose();
}
Then you can call this method from a MouseAdapter or KeyAdapter, depending on where you want to get your String from. In your case it could be MouseAdapter, which listens to doubleclicks, gets the current cursor position of the text, marks the word and then adds the String to the clipboard.
edit to answer a question: You can set up your own MouseAdapater and attach it to buttons, text fields or whateer you like. Here's an example for a button:
Button btnGo1 = new Button(parent, SWT.NONE);
btnGo1.setText("Go");
btnGo1.addMouseListener(new MouseAdapter() {
#Override
public void mouseDoubleClick(MouseEvent e) {
//do what you want to do in here
}
});
If you want to implement mouseUp and mouseDown events, too, you can just add MouseListenerinstead of the Adapter. The only advantage of the Adapter is, that you don't have to override the other methods of the interface.
Since the original question was to automatically get the selection of the text of an editor: the way to get the selection from an editor is explained here.
You can try this plugin. Along with auto copy points mentioned in Eclipse show number of lines and/or file size also addressed.

Using GQuery in GWT widgets

I am using the GWT application widget library and want to validate the controls in the button click event.The code I am writing
GQuery input = $(e).filter("input[type='password']").widgets();
but its is giving me compile time error.Please tell me or refer me any tutorial for validating the widget library controls.
the widgets() method returns a list of widget and not a GQuery object
List<Widget> myPasswordInputs = $(e).filter("input[type='password']").widgets();
If you are only one input of type password you can maybe use directly widget() method :
PasswordTextBox myPasswordInput = $(e).filter("input[type='password']").widget();
Question: are you sure of your '$(e).filter("input[type='password']")' ?
Because it means : "Create a GQuery object containing my element 'e' and keep it only if 'e' is an input of type password"
If you want to retrieve all password inputs present within an element e, you have to use :
List<Widget> myPasswordInputs = $("input[type='password']",e).widgets();
Julien
Try:
GQuery input = GQuery.$(e).filter("input[type='password']").widgets();
You need to do a static import to use $ directly:
import static com.google.gwt.query.client.GQuery.*;
import static com.google.gwt.query.client.css.CSS.*;