/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.editor.scriptdoc; import java.util.Map; import com.aptana.ide.core.ui.CoreUIUtils; import com.aptana.ide.editor.js.JSLanguageEnvironment; import com.aptana.ide.editor.js.JSOffsetMapper; import com.aptana.ide.editor.js.runtime.Environment; import com.aptana.ide.editor.js.runtime.IObject; import com.aptana.ide.editor.js.runtime.JSUndefined; import com.aptana.ide.editor.js.runtime.Property; import com.aptana.ide.editor.scriptdoc.lexing.ScriptDocTokenTypes; import com.aptana.ide.editor.scriptdoc.parsing.ScriptDocMimeType; import com.aptana.ide.editor.scriptdoc.parsing.ScriptDocParseState; import com.aptana.ide.editors.managers.EnvironmentManager; import com.aptana.ide.editors.managers.FileContextManager; import com.aptana.ide.editors.unified.ChildOffsetMapper; import com.aptana.ide.editors.unified.IChildOffsetMapper; import com.aptana.ide.editors.unified.IParentOffsetMapper; import com.aptana.ide.lexer.Lexeme; import com.aptana.ide.lexer.LexemeList; import com.aptana.ide.metadata.IDocumentation; import com.aptana.ide.metadata.IDocumentationStore; import com.aptana.ide.parsing.CodeLocation; import com.aptana.ide.parsing.ICodeLocation; /** * */ public class ScriptDocOffsetMapper extends ChildOffsetMapper implements IChildOffsetMapper { /** * @param parent */ public ScriptDocOffsetMapper(IParentOffsetMapper parent) { super(parent); } /** * @see com.aptana.ide.editors.unified.ChildOffsetMapper#findTarget(com.aptana.ide.lexer.Lexeme) */ public ICodeLocation findTarget(Lexeme lexeme) { ICodeLocation codeLoc = getIDTargetFromOffset(lexeme); // need source to find the @id location //this.getFileService().getParseState().getSource() return codeLoc; } /** * This only works for @id name.name tags * @return Returns the @id identifier */ private ICodeLocation getIDTargetFromOffset(Lexeme lexeme) { ICodeLocation result = null; ScriptDocParseState ps = (ScriptDocParseState)this.getFileService().getParseState().getParseState(ScriptDocMimeType.MimeType); Lexeme lastLexeme = getLastDocLexeme(ps, lexeme); int offset = lastLexeme.getEndingOffset(); if(offset == -1) { return null; } String type = getTypeName(ps, lexeme); if(!type.equals("")) //$NON-NLS-1$ { result = getType(type, lexeme); } else { result = getID(ps, lexeme, offset); } return result; } private ICodeLocation getType(String fullName, Lexeme lexeme) { ICodeLocation result = null; Environment jsEnv = (Environment)JSLanguageEnvironment.getInstance().getRuntimeEnvironment(); IObject global = jsEnv.getGlobal(); Property prop = global.getProperty(fullName); if(prop == null) { return null; } IObject obj = prop.getValue(FileContextManager.CURRENT_FILE_INDEX, Integer.MAX_VALUE); if(obj == JSUndefined.getSingletonInstance()) { return null; } result = JSOffsetMapper.findTargetFromName(obj, prop); return result; } private ICodeLocation getID(ScriptDocParseState ps, Lexeme lexeme, int offset) { ICodeLocation result = null; IDocumentationStore store = ps.getDocumentationStore(); //int offset = lexeme.getStartingOffset(); IDocumentation doc = store.getDocumentationFromOffset(offset); String[] ids = doc.getIDs(); if(doc != null && ids.length > 0) { String id = ids[0]; // todo: find codeLoc closest to the passed lexeme Map<String, IDocumentation> idMap = EnvironmentManager.getDocumentationIdMap(); if(idMap.containsKey(id)) { IDocumentation mapDoc = idMap.get(id); CodeLocation[] locs = mapDoc.getID(id); for (int i = 0; i < locs.length; i++) { CodeLocation location = locs[i]; // TODO: the fullPath of CodeLocation should use URIs String locationURI = CoreUIUtils.getURI(location.getFullPath()); String psURI = FileContextManager.getURIFromFileIndex(ps.getFileIndex()); // filter out the current script doc comment if((location.getStartLexeme().getEndingOffset() != lexeme.getEndingOffset()) || !locationURI.equals(psURI)) { result = location; break; } } } } return result; } private String getTypeName(ScriptDocParseState ps, Lexeme lexeme) { String result = ""; //$NON-NLS-1$ Lexeme orgLexeme = lexeme; LexemeList ll = ps.getLexemeList(); int index = ll.getLexemeIndex(lexeme); while(lexeme != null && lexeme.getLanguage().equals(ScriptDocMimeType.MimeType)) { if( lexeme.typeIndex == ScriptDocTokenTypes.COMMA || lexeme.typeIndex == ScriptDocTokenTypes.IDENTIFIER || lexeme.typeIndex == ScriptDocTokenTypes.ELLIPSIS ) { // keep looking for brace index = (index == 0) ? 0 : index; } else if(lexeme.typeIndex == ScriptDocTokenTypes.LCURLY) { result = orgLexeme.getText(); break; } else { break; } if(index > 0) { index--; lexeme = ll.get(index); } else { break; } } return result; } private Lexeme getLastDocLexeme(ScriptDocParseState ps, Lexeme lexeme) { Lexeme result = null; LexemeList ll = ps.getLexemeList(); int index = ll.getLexemeIndex(lexeme); while(lexeme != null && lexeme.getLanguage().equals(ScriptDocMimeType.MimeType)) { if(lexeme.typeIndex == ScriptDocTokenTypes.END_DOCUMENTATION) { result = lexeme; break; } if(index < ll.size()) { index++; lexeme = ll.get(index); } else { break; } } return result; } }