Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: Source/devtools/front_end/CodeMirrorTextEditor.js

Issue 15986003: DevTools: [CodeMirror] autocompletion for CodeMirrorTextEditor (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: address comments Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | Source/devtools/front_end/CompletionDictionary.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 WebInspector.CodeMirrorTextEditor = function(url, delegate) 48 WebInspector.CodeMirrorTextEditor = function(url, delegate)
49 { 49 {
50 WebInspector.View.call(this); 50 WebInspector.View.call(this);
51 this._delegate = delegate; 51 this._delegate = delegate;
52 this._url = url; 52 this._url = url;
53 53
54 this.registerRequiredCSS("cm/codemirror.css"); 54 this.registerRequiredCSS("cm/codemirror.css");
55 this.registerRequiredCSS("cm/showhint.css"); 55 this.registerRequiredCSS("cm/showhint.css");
56 this.registerRequiredCSS("cm/cmdevtools.css"); 56 this.registerRequiredCSS("cm/cmdevtools.css");
57 57
58 function autocompleteCommand()
59 {
60 if (!this._dictionary || this._codeMirror.somethingSelected())
61 return;
62 CodeMirror.showHint(this._codeMirror, this._autocomplete.bind(this));
63 }
64 CodeMirror.commands.autocomplete = autocompleteCommand.bind(this);
65
58 this._codeMirror = window.CodeMirror(this.element, { 66 this._codeMirror = window.CodeMirror(this.element, {
59 lineNumbers: true, 67 lineNumbers: true,
60 gutters: ["CodeMirror-linenumbers"], 68 gutters: ["CodeMirror-linenumbers"],
61 matchBrackets: true, 69 matchBrackets: true,
62 smartIndent: false, 70 smartIndent: false,
63 styleSelectedText: true, 71 styleSelectedText: true,
64 electricChars: false, 72 electricChars: false,
65 autoCloseBrackets: true 73 autoCloseBrackets: true
66 }); 74 });
67 75
68 var extraKeys = {}; 76 var extraKeys = {"Ctrl-Space": "autocomplete"};
69 var indent = WebInspector.settings.textEditorIndent.get(); 77 var indent = WebInspector.settings.textEditorIndent.get();
70 if (indent === WebInspector.TextUtils.Indent.TabCharacter) { 78 if (indent === WebInspector.TextUtils.Indent.TabCharacter) {
71 this._codeMirror.setOption("indentWithTabs", true); 79 this._codeMirror.setOption("indentWithTabs", true);
72 this._codeMirror.setOption("indentUnit", 4); 80 this._codeMirror.setOption("indentUnit", 4);
73 } else { 81 } else {
74 this._codeMirror.setOption("indentWithTabs", false); 82 this._codeMirror.setOption("indentWithTabs", false);
75 this._codeMirror.setOption("indentUnit", indent.length); 83 this._codeMirror.setOption("indentUnit", indent.length);
76 extraKeys.Tab = function(codeMirror) 84 extraKeys.Tab = function(codeMirror)
77 { 85 {
78 if (codeMirror.somethingSelected()) 86 if (codeMirror.somethingSelected())
79 return CodeMirror.Pass; 87 return CodeMirror.Pass;
80 codeMirror.replaceRange(indent, codeMirror.getCursor()); 88 codeMirror.replaceRange(indent, codeMirror.getCursor());
81 } 89 }
82 } 90 }
83 this._codeMirror.setOption("extraKeys", extraKeys); 91 this._codeMirror.setOption("extraKeys", extraKeys);
84 92
85 this._tokenHighlighter = new WebInspector.CodeMirrorTextEditor.TokenHighligh ter(this._codeMirror); 93 this._tokenHighlighter = new WebInspector.CodeMirrorTextEditor.TokenHighligh ter(this._codeMirror);
86 this._blockIndentController = new WebInspector.CodeMirrorTextEditor.BlockInd entController(this._codeMirror); 94 this._blockIndentController = new WebInspector.CodeMirrorTextEditor.BlockInd entController(this._codeMirror);
87 this._fixWordMovement = new WebInspector.CodeMirrorTextEditor.FixWordMovemen t(this._codeMirror); 95 this._fixWordMovement = new WebInspector.CodeMirrorTextEditor.FixWordMovemen t(this._codeMirror);
88 96
89 this._codeMirror.on("change", this._change.bind(this)); 97 this._codeMirror.on("change", this._change.bind(this));
98 this._codeMirror.on("beforeChange", this._beforeChange.bind(this));
90 this._codeMirror.on("gutterClick", this._gutterClick.bind(this)); 99 this._codeMirror.on("gutterClick", this._gutterClick.bind(this));
91 this._codeMirror.on("cursorActivity", this._cursorActivity.bind(this)); 100 this._codeMirror.on("cursorActivity", this._cursorActivity.bind(this));
92 this._codeMirror.on("scroll", this._scroll.bind(this)); 101 this._codeMirror.on("scroll", this._scroll.bind(this));
93 this.element.addEventListener("contextmenu", this._contextMenu.bind(this)); 102 this.element.addEventListener("contextmenu", this._contextMenu.bind(this));
94 103
95 this.element.firstChild.addStyleClass("source-code"); 104 this.element.firstChild.addStyleClass("source-code");
96 this.element.firstChild.addStyleClass("fill"); 105 this.element.firstChild.addStyleClass("fill");
97 this._elementToWidget = new Map(); 106 this._elementToWidget = new Map();
98 this._nestedUpdatesCounter = 0; 107 this._nestedUpdatesCounter = 0;
99 108
100 this.element.addEventListener("focus", this._handleElementFocus.bind(this), false); 109 this.element.addEventListener("focus", this._handleElementFocus.bind(this), false);
110 this.element.addEventListener("keydown", this._handleKeyDown.bind(this), fal se);
101 this.element.tabIndex = 0; 111 this.element.tabIndex = 0;
102 this._setupSelectionColor(); 112 this._setupSelectionColor();
103 } 113 }
104 114
105 WebInspector.CodeMirrorTextEditor.prototype = { 115 WebInspector.CodeMirrorTextEditor.prototype = {
106 116
107 undo: function() 117 undo: function()
108 { 118 {
109 this._codeMirror.undo(); 119 this._codeMirror.undo();
110 }, 120 },
(...skipping 13 matching lines...) Expand all
124 var foregroundColor = WebInspector.getSelectionForegroundColor(); 134 var foregroundColor = WebInspector.getSelectionForegroundColor();
125 var foregroundColorRule = foregroundColor ? ".CodeMirror .CodeMirror-sel ectedtext { color: " + foregroundColor + "!important;}" : ""; 135 var foregroundColorRule = foregroundColor ? ".CodeMirror .CodeMirror-sel ectedtext { color: " + foregroundColor + "!important;}" : "";
126 if (!foregroundColorRule && !backgroundColorRule) 136 if (!foregroundColorRule && !backgroundColorRule)
127 return; 137 return;
128 138
129 var style = document.createElement("style"); 139 var style = document.createElement("style");
130 style.textContent = backgroundColorRule + foregroundColorRule; 140 style.textContent = backgroundColorRule + foregroundColorRule;
131 document.head.appendChild(style); 141 document.head.appendChild(style);
132 }, 142 },
133 143
144 _autocomplete: function(codeMirror)
145 {
146 var cursor = codeMirror.getCursor();
147 var prefixRange = this._wordRangeForCursorPosition(cursor.line, cursor.c h, true);
148 if (!prefixRange)
149 return null;
150 var prefix = this.copyRange(prefixRange);
151 this._dictionary.removeWord(prefix);
152 var wordsWithPrefix = this._dictionary.wordsWithPrefix(this.copyRange(pr efixRange));
153 this._dictionary.addWord(prefix);
154
155 var data = {
156 list: wordsWithPrefix,
157 from: new CodeMirror.Pos(prefixRange.startLine, prefixRange.startCol umn),
158 to: new CodeMirror.Pos(prefixRange.endLine, prefixRange.endColumn)
159 };
160 CodeMirror.on(data, "close", this._handleAutocompletionClose.bind(this)) ;
161
162 return data;
163 },
164
165 _handleKeyDown: function(e)
166 {
167 if (!!this._consumeEsc && e.keyCode === WebInspector.KeyboardShortcut.Ke ys.Esc.code)
168 e.consume(true);
169 delete this._consumeEsc;
170 },
171
172 _handleAutocompletionClose: function()
173 {
174 this._consumeEsc = true;
175 },
176
177 /**
178 * @param {string} text
179 */
180 _addTextToCompletionDictionary: function(text)
181 {
182 var words = WebInspector.TextUtils.textToWords(text);
183 for(var i = 0; i < words.length; ++i) {
184 this._dictionary.addWord(words[i]);
185 }
186 },
187
188 /**
189 * @param {string} text
190 */
191 _removeTextFromCompletionDictionary: function(text)
192 {
193 var words = WebInspector.TextUtils.textToWords(text);
194 for(var i = 0; i < words.length; ++i) {
195 this._dictionary.removeWord(words[i]);
196 }
197 },
198
134 /** 199 /**
135 * @param {WebInspector.CompletionDictionary} dictionary 200 * @param {WebInspector.CompletionDictionary} dictionary
136 */ 201 */
137 setCompletionDictionary: function(dictionary) { }, 202 setCompletionDictionary: function(dictionary)
203 {
204 this._dictionary = dictionary;
205 this._addTextToCompletionDictionary(this.text());
206 },
138 207
139 /** 208 /**
140 * @param {number} lineNumber 209 * @param {number} lineNumber
141 * @param {number} column 210 * @param {number} column
142 * @return {?{x: number, y: number, height: number}} 211 * @return {?{x: number, y: number, height: number}}
143 */ 212 */
144 cursorPositionToCoordinates: function(lineNumber, column) 213 cursorPositionToCoordinates: function(lineNumber, column)
145 { 214 {
146 if (lineNumber >= this._codeMirror.lineCount || column > this._codeMirro r.getLine(lineNumber).length || lineNumber < 0 || column < 0) 215 if (lineNumber >= this._codeMirror.lineCount || column > this._codeMirro r.getLine(lineNumber).length || lineNumber < 0 || column < 0)
147 return null; 216 return null;
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 */ 556 */
488 editRange: function(range, text) 557 editRange: function(range, text)
489 { 558 {
490 var pos = this._toPos(range); 559 var pos = this._toPos(range);
491 this._codeMirror.replaceRange(text, pos.start, pos.end); 560 this._codeMirror.replaceRange(text, pos.start, pos.end);
492 var newRange = this._toRange(pos.start, this._codeMirror.posFromIndex(th is._codeMirror.indexFromPos(pos.start) + text.length)); 561 var newRange = this._toRange(pos.start, this._codeMirror.posFromIndex(th is._codeMirror.indexFromPos(pos.start) + text.length));
493 this._delegate.onTextChanged(range, newRange); 562 this._delegate.onTextChanged(range, newRange);
494 return newRange; 563 return newRange;
495 }, 564 },
496 565
566 /**
567 * @param {number} lineNumber
568 * @param {number} column
569 * @param {boolean=} prefixOnly
570 * @return {?WebInspector.TextRange}
571 */
572 _wordRangeForCursorPosition: function(lineNumber, column, prefixOnly)
573 {
574 var line = this.line(lineNumber);
575 if (!WebInspector.TextUtils.isWordChar(line.charAt(column - 1)))
576 return null;
577 var wordStart = column - 1;
578 while(wordStart > 0 && WebInspector.TextUtils.isWordChar(line.charAt(wor dStart - 1)))
579 --wordStart;
580 if (prefixOnly)
581 return new WebInspector.TextRange(lineNumber, wordStart, lineNumber, column);
582 var wordEnd = column;
583 while(wordEnd < line.length && WebInspector.TextUtils.isWordChar(line.ch arAt(wordEnd)))
584 ++wordEnd;
585 return new WebInspector.TextRange(lineNumber, wordStart, lineNumber, wor dEnd);
586 },
587
588 _beforeChange: function(codeMirror, changeObject)
589 {
590 if (!this._dictionary)
591 return;
592 this._updatedLines = this._updatedLines || {};
593 for(var i = changeObject.from.line; i <= changeObject.to.line; ++i)
594 this._updatedLines[i] = this.line(i);
595 },
596
497 _change: function(codeMirror, changeObject) 597 _change: function(codeMirror, changeObject)
498 { 598 {
499 var widgets = this._elementToWidget.values(); 599 var widgets = this._elementToWidget.values();
500 for (var i = 0; i < widgets.length; ++i) 600 for (var i = 0; i < widgets.length; ++i)
501 this._codeMirror.removeLineWidget(widgets[i]); 601 this._codeMirror.removeLineWidget(widgets[i]);
502 this._elementToWidget.clear(); 602 this._elementToWidget.clear();
503 603
604 if (this._updatedLines) {
605 for(var lineNumber in this._updatedLines)
606 this._removeTextFromCompletionDictionary(this._updatedLines[line Number]);
607 delete this._updatedLines;
608 }
609
610 var linesToUpdate = {};
504 do { 611 do {
505 var oldRange = this._toRange(changeObject.from, changeObject.to); 612 var oldRange = this._toRange(changeObject.from, changeObject.to);
506 var newRange = oldRange.clone(); 613 var newRange = oldRange.clone();
507 var linesAdded = changeObject.text.length; 614 var linesAdded = changeObject.text.length;
508 if (linesAdded === 0) { 615 if (linesAdded === 0) {
509 newRange.endLine = newRange.startLine; 616 newRange.endLine = newRange.startLine;
510 newRange.endColumn = newRange.startColumn; 617 newRange.endColumn = newRange.startColumn;
511 } else if (linesAdded === 1) { 618 } else if (linesAdded === 1) {
512 newRange.endLine = newRange.startLine; 619 newRange.endLine = newRange.startLine;
513 newRange.endColumn = newRange.startColumn + changeObject.text[0] .length; 620 newRange.endColumn = newRange.startColumn + changeObject.text[0] .length;
514 } else { 621 } else {
515 newRange.endLine = newRange.startLine + linesAdded - 1; 622 newRange.endLine = newRange.startLine + linesAdded - 1;
516 newRange.endColumn = changeObject.text[linesAdded - 1].length; 623 newRange.endColumn = changeObject.text[linesAdded - 1].length;
517 } 624 }
518 625
519 if (!this._muteTextChangedEvent) 626 if (!this._muteTextChangedEvent)
520 this._delegate.onTextChanged(oldRange, newRange); 627 this._delegate.onTextChanged(oldRange, newRange);
521 628
629 for(var i = newRange.startLine; i <= newRange.endLine; ++i) {
630 linesToUpdate[i] = true;
631 }
632 if (this._dictionary) {
633 for(var i = newRange.startLine; i <= newRange.endLine; ++i)
634 linesToUpdate[i] = this.line(i);
635 }
522 } while (changeObject = changeObject.next); 636 } while (changeObject = changeObject.next);
637 if (this._dictionary) {
638 for(var lineNumber in linesToUpdate)
639 this._addTextToCompletionDictionary(linesToUpdate[lineNumber]);
640 }
523 }, 641 },
524 642
525 _cursorActivity: function() 643 _cursorActivity: function()
526 { 644 {
527 var start = this._codeMirror.getCursor("anchor"); 645 var start = this._codeMirror.getCursor("anchor");
528 var end = this._codeMirror.getCursor("head"); 646 var end = this._codeMirror.getCursor("head");
529 this._delegate.selectionChanged(this._toRange(start, end)); 647 this._delegate.selectionChanged(this._toRange(start, end));
530 }, 648 },
531 649
532 _coordsCharLocal: function(coords) 650 _coordsCharLocal: function(coords)
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after
874 var modifierKey = WebInspector.isMac() ? "Alt" : "Ctrl"; 992 var modifierKey = WebInspector.isMac() ? "Alt" : "Ctrl";
875 var leftKey = modifierKey + "-Left"; 993 var leftKey = modifierKey + "-Left";
876 var rightKey = modifierKey + "-Right"; 994 var rightKey = modifierKey + "-Right";
877 var keyMap = {}; 995 var keyMap = {};
878 keyMap[leftKey] = moveLeft.bind(this, false); 996 keyMap[leftKey] = moveLeft.bind(this, false);
879 keyMap[rightKey] = moveRight.bind(this, false); 997 keyMap[rightKey] = moveRight.bind(this, false);
880 keyMap["Shift-" + leftKey] = moveLeft.bind(this, true); 998 keyMap["Shift-" + leftKey] = moveLeft.bind(this, true);
881 keyMap["Shift-" + rightKey] = moveRight.bind(this, true); 999 keyMap["Shift-" + rightKey] = moveRight.bind(this, true);
882 codeMirror.addKeyMap(keyMap); 1000 codeMirror.addKeyMap(keyMap);
883 } 1001 }
OLDNEW
« no previous file with comments | « no previous file | Source/devtools/front_end/CompletionDictionary.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698